mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 02:17:31 +00:00 
			
		
		
		
	Separate multipliers per claimed denom (#971)
* move multipliers to their own file * add multipliers per denom to MsgClaimHardReward * claim with multipliers per denom for hard claims * remove unused error * add multipliers per denom to params connect to to hard claiming * temporary fix migration * update usdx claiming for new multiplier params * claim with multipliers per denom for delegator * claim with multipliers per denom for swap rewards * tidy up multiplier name validation * rename new multipliers field in params * remove old multpliers from params * clear up various TODOs * add tags to new structs * remove dead code
This commit is contained in:
		
							parent
							
								
									6bcc843c2e
								
							
						
					
					
						commit
						63c8421a81
					
				@ -15,6 +15,20 @@ func Incentive(incentiveGS v0_14incentive.GenesisState) v0_15incentive.GenesisSt
 | 
			
		||||
		newMultiplier := v0_15incentive.NewMultiplier(v0_15incentive.MultiplierName(m.Name), m.MonthsLockup, m.Factor)
 | 
			
		||||
		claimMultipliers = append(claimMultipliers, newMultiplier)
 | 
			
		||||
	}
 | 
			
		||||
	newMultipliers := v0_15incentive.MultipliersPerDenom{
 | 
			
		||||
		{
 | 
			
		||||
			Denom:       "hard",
 | 
			
		||||
			Multipliers: claimMultipliers,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Denom:       "ukava",
 | 
			
		||||
			Multipliers: claimMultipliers,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Denom:       "swp",
 | 
			
		||||
			Multipliers: claimMultipliers, // TODO set the correct multipliers
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	usdxMintingRewardPeriods := v0_15incentive.RewardPeriods{}
 | 
			
		||||
	for _, rp := range incentiveGS.Params.USDXMintingRewardPeriods {
 | 
			
		||||
@ -41,7 +55,7 @@ func Incentive(incentiveGS v0_14incentive.GenesisState) v0_15incentive.GenesisSt
 | 
			
		||||
		migrateMultiRewardPeriods(incentiveGS.Params.HardBorrowRewardPeriods),
 | 
			
		||||
		hardDelegatorRewardPeriods,
 | 
			
		||||
		swapRewardPeriods,
 | 
			
		||||
		claimMultipliers,
 | 
			
		||||
		newMultipliers,
 | 
			
		||||
		incentiveGS.Params.ClaimEnd,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,9 +52,7 @@ var (
 | 
			
		||||
	NewQuerier                           = keeper.NewQuerier
 | 
			
		||||
	DefaultGenesisState                  = types.DefaultGenesisState
 | 
			
		||||
	DefaultParams                        = types.DefaultParams
 | 
			
		||||
	FilterCoins                          = types.FilterCoins
 | 
			
		||||
	GetTotalVestingPeriodLength          = types.GetTotalVestingPeriodLength
 | 
			
		||||
	MultiplyCoins                        = types.MultiplyCoins
 | 
			
		||||
	NewAccumulationTime                  = types.NewAccumulationTime
 | 
			
		||||
	NewAccumulator                       = types.NewAccumulator
 | 
			
		||||
	NewDelegatorClaim                    = types.NewDelegatorClaim
 | 
			
		||||
@ -79,9 +77,12 @@ var (
 | 
			
		||||
	NewQueryRewardsParams                = types.NewQueryRewardsParams
 | 
			
		||||
	NewRewardIndex                       = types.NewRewardIndex
 | 
			
		||||
	NewRewardPeriod                      = types.NewRewardPeriod
 | 
			
		||||
	NewSelection                         = types.NewSelection
 | 
			
		||||
	NewSelectionsFromMap                 = types.NewSelectionsFromMap
 | 
			
		||||
	NewSwapClaim                         = types.NewSwapClaim
 | 
			
		||||
	NewUSDXMintingClaim                  = types.NewUSDXMintingClaim
 | 
			
		||||
	ParamKeyTable                        = types.ParamKeyTable
 | 
			
		||||
	ParseMultiplierName                  = types.ParseMultiplierName
 | 
			
		||||
	RegisterCodec                        = types.RegisterCodec
 | 
			
		||||
 | 
			
		||||
	// variable aliases
 | 
			
		||||
@ -105,7 +106,6 @@ var (
 | 
			
		||||
	ErrInsufficientModAccountBalance              = types.ErrInsufficientModAccountBalance
 | 
			
		||||
	ErrInvalidAccountType                         = types.ErrInvalidAccountType
 | 
			
		||||
	ErrInvalidClaimDenoms                         = types.ErrInvalidClaimDenoms
 | 
			
		||||
	ErrInvalidClaimOwner                          = types.ErrInvalidClaimOwner
 | 
			
		||||
	ErrInvalidClaimType                           = types.ErrInvalidClaimType
 | 
			
		||||
	ErrInvalidMultiplier                          = types.ErrInvalidMultiplier
 | 
			
		||||
	ErrNoClaimsFound                              = types.ErrNoClaimsFound
 | 
			
		||||
@ -171,6 +171,7 @@ type (
 | 
			
		||||
	Multiplier                        = types.Multiplier
 | 
			
		||||
	MultiplierName                    = types.MultiplierName
 | 
			
		||||
	Multipliers                       = types.Multipliers
 | 
			
		||||
	MultipliersPerDenom               = types.MultipliersPerDenom
 | 
			
		||||
	ParamSubspace                     = types.ParamSubspace
 | 
			
		||||
	Params                            = types.Params
 | 
			
		||||
	QueryGetRewardFactorsResponse     = types.QueryGetRewardFactorsResponse
 | 
			
		||||
@ -179,6 +180,8 @@ type (
 | 
			
		||||
	RewardIndexes                     = types.RewardIndexes
 | 
			
		||||
	RewardPeriod                      = types.RewardPeriod
 | 
			
		||||
	RewardPeriods                     = types.RewardPeriods
 | 
			
		||||
	Selection                         = types.Selection
 | 
			
		||||
	Selections                        = types.Selections
 | 
			
		||||
	StakingKeeper                     = types.StakingKeeper
 | 
			
		||||
	SupplyKeeper                      = types.SupplyKeeper
 | 
			
		||||
	SwapClaim                         = types.SwapClaim
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,9 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const multiplierFlag = "multiplier"
 | 
			
		||||
const multiplierFlagShort = "m"
 | 
			
		||||
 | 
			
		||||
// GetTxCmd returns the transaction cli commands for the incentive module
 | 
			
		||||
func GetTxCmd(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	incentiveTxCmd := &cobra.Command{
 | 
			
		||||
@ -100,84 +103,118 @@ A receiver address for the rewards is needed as validator vesting accounts canno
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimHard(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:     "claim-hard [multiplier]",
 | 
			
		||||
		Short:   "claim sender's Hard module rewards using a given multiplier",
 | 
			
		||||
		Long:    `Claim sender's outstanding Hard rewards for deposit/borrow using given multiplier`,
 | 
			
		||||
		Example: fmt.Sprintf(`  $ %s tx %s claim-hard large`, version.ClientName, types.ModuleName),
 | 
			
		||||
		Args:    cobra.ExactArgs(1),
 | 
			
		||||
		Use:   "claim-hard",
 | 
			
		||||
		Short: "claim sender's Hard module rewards using given multipliers",
 | 
			
		||||
		Long:  `Claim sender's outstanding Hard rewards for deposit/borrow using given multipliers`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-hard --%s hard=large --%s ukava=small`, version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-hard --%s hard=large,ukava=small`, version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.NoArgs,
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimHardReward(sender, multiplier, denomsToClaim)
 | 
			
		||||
			msg := types.NewMsgClaimHardReward(sender, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimHardVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-hard-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim Hard module rewards on behalf of a validator vesting account using a given multiplier",
 | 
			
		||||
		Long: `Claim sender's outstanding hard supply/borrow rewards on behalf of a validator vesting account using given multiplier
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.
 | 
			
		||||
Optionally claim only certain denoms from the rewards. Specifying none will claim all of them.`,
 | 
			
		||||
		Use:   "claim-hard-vesting [receiver]",
 | 
			
		||||
		Short: "claim Hard module rewards on behalf of a validator vesting account using given multipliers",
 | 
			
		||||
		Long: `Claim sender's outstanding hard supply/borrow rewards on behalf of a validator vesting account using given multipliers
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting small kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --claim-only swp,hard", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s hard=large --%s ukava=small", version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s hard=large,ukava=small", version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
		Args: cobra.ExactArgs(1),
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
			receiverStr := args[1]
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(receiverStr)
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(args[1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimHardRewardVVesting(sender, receiver, multiplier, denomsToClaim)
 | 
			
		||||
			msg := types.NewMsgClaimHardRewardVVesting(sender, receiver, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimDelegator(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-delegator [multiplier]",
 | 
			
		||||
		Short: "claim sender's delegator rewards using a given multiplier",
 | 
			
		||||
		Long: `Claim sender's outstanding delegator rewards using given multiplier.
 | 
			
		||||
Optionally claim only certain denoms from the rewards. Specifying none will claim all of them.`,
 | 
			
		||||
		Use:   "claim-delegator",
 | 
			
		||||
		Short: "claim sender's non-sdk delegator rewards using given multipliers",
 | 
			
		||||
		Long:  `Claim sender's outstanding delegator rewards using given multipliers`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-delegator large", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-delegator large --claim-only swp,hard", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-delegator --%s hard=large --%s swp=small`, version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-delegator --%s hard=large,swp=small`, version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.NoArgs,
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorReward(sender, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimDelegatorVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-delegator-vesting [receiver]",
 | 
			
		||||
		Short: "claim non-sdk delegator rewards on behalf of a validator vesting account using given multipliers",
 | 
			
		||||
		Long: `Claim sender's outstanding delegator rewards on behalf of a validator vesting account using given multipliers
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s hard=large --%s swp=small", version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s hard=large,swp=small", version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.ExactArgs(1),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
@ -186,70 +223,68 @@ Optionally claim only certain denoms from the rewards. Specifying none will clai
 | 
			
		||||
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorReward(sender, multiplier, denomsToClaim)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimDelegatorVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-delegator-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim delegator rewards on behalf of a validator vesting account using a given multiplier",
 | 
			
		||||
		Long: `Claim sender's outstanding delegator rewards on behalf of a validator vesting account using given multiplier
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.
 | 
			
		||||
Optionally claim only certain denoms from the rewards. Specifying none will claim all of them.`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-delegator-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-delegator-vesting small kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --claim-only swp,hard", version.ClientName, types.ModuleName),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
			receiverStr := args[1]
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(receiverStr)
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(args[1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorRewardVVesting(sender, receiver, multiplier, denomsToClaim)
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorRewardVVesting(sender, receiver, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimSwap(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-swap [multiplier]",
 | 
			
		||||
		Short: "claim sender's swap rewards using a given multiplier",
 | 
			
		||||
		Long: `Claim sender's outstanding swap rewards using given multiplier.
 | 
			
		||||
Optionally claim only certain denoms from the rewards. Specifying none will claim all of them.`,
 | 
			
		||||
		Use:   "claim-swap",
 | 
			
		||||
		Short: "claim sender's swap rewards using given multipliers",
 | 
			
		||||
		Long:  `Claim sender's outstanding swap rewards using given multipliers`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-swap large", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-swap large --claim-only swp,hard", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-hard --%s swp=large --%s ukava=small`, version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf(`  $ %s tx %s claim-hard --%s swp=large,ukava=small`, version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.NoArgs,
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimSwapReward(sender, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimSwapVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim map[string]string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-swap-vesting [receiver]",
 | 
			
		||||
		Short: "claim swap rewards on behalf of a validator vesting account using given multipliers",
 | 
			
		||||
		Long: `Claim sender's outstanding swap rewards on behalf of a validator vesting account using given multipliers
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s ukava=large --%s swp=small", version.ClientName, types.ModuleName, multiplierFlag, multiplierFlag),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-hard-vesting kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --%s ukava=large,swp=small", version.ClientName, types.ModuleName, multiplierFlag),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.ExactArgs(1),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
@ -258,55 +293,21 @@ Optionally claim only certain denoms from the rewards. Specifying none will clai
 | 
			
		||||
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimSwapReward(sender, multiplier, denomsToClaim)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimSwapVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-swap-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim swap rewards on behalf of a validator vesting account using a given multiplier",
 | 
			
		||||
		Long: `Claim sender's outstanding swap rewards on behalf of a validator vesting account using given multiplier
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.
 | 
			
		||||
Optionally claim only certain denoms from the rewards. Specifying none will claim all of them.`,
 | 
			
		||||
		Example: strings.Join([]string{
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-swap-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw", version.ClientName, types.ModuleName),
 | 
			
		||||
			fmt.Sprintf("  $ %s tx %s claim-swap-vesting small kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw --claim-only swp,hard", version.ClientName, types.ModuleName),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		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))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
			receiverStr := args[1]
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(receiverStr)
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(args[1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			selections := types.NewSelectionsFromMap(denomsToClaim)
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimSwapRewardVVesting(sender, receiver, multiplier, denomsToClaim)
 | 
			
		||||
			msg := types.NewMsgClaimSwapRewardVVesting(sender, receiver, selections...)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmd.Flags().StringSliceVar(&denomsToClaim, "claim-only", nil, "claim only these denoms, otherwise claim all denoms")
 | 
			
		||||
	cmd.Flags().StringToStringVarP(&denomsToClaim, multiplierFlag, multiplierFlagShort, nil, "specify the denoms to claim, each with a multiplier lockup")
 | 
			
		||||
	cmd.MarkFlagRequired(multiplierFlag)
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,8 @@ import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// REST variable names
 | 
			
		||||
@ -22,17 +24,15 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	BaseReq       rest.BaseReq     `json:"base_req" yaml:"base_req"`
 | 
			
		||||
	Sender        sdk.AccAddress   `json:"sender" yaml:"sender"`
 | 
			
		||||
	DenomsToClaim types.Selections `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostClaimReq defines the properties of claim transaction's request body.
 | 
			
		||||
type PostClaimVVestingReq struct {
 | 
			
		||||
	BaseReq        rest.BaseReq   `json:"base_req" yaml:"base_req"`
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver       sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	BaseReq       rest.BaseReq     `json:"base_req" yaml:"base_req"`
 | 
			
		||||
	Sender        sdk.AccAddress   `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver      sdk.AccAddress   `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	DenomsToClaim types.Selections `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,20 +29,23 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	r.HandleFunc("/incentive/claim-swap-vesting", postClaimVVestingHandlerFn(cliCtx, swapVVGenerator)).Methods("POST")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func usdxMintingGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingReward(req.Sender, req.MultiplierName)
 | 
			
		||||
func usdxMintingGenerator(req PostClaimReq) (sdk.Msg, error) {
 | 
			
		||||
	if len(req.DenomsToClaim) != 1 {
 | 
			
		||||
		return nil, fmt.Errorf("must only claim %s denom for usdx minting rewards, got '%s", types.USDXMintingRewardDenom, req.DenomsToClaim)
 | 
			
		||||
	}
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingReward(req.Sender, req.DenomsToClaim[0].MultiplierName), nil
 | 
			
		||||
}
 | 
			
		||||
func hardGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimHardReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func hardGenerator(req PostClaimReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimHardReward(req.Sender, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
func delegatorGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimDelegatorReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func delegatorGenerator(req PostClaimReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimDelegatorReward(req.Sender, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
func swapGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimSwapReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func swapGenerator(req PostClaimReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimSwapReward(req.Sender, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimReq) sdk.Msg) http.HandlerFunc {
 | 
			
		||||
func postClaimHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimReq) (sdk.Msg, error)) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
@ -65,7 +68,12 @@ func postClaimHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostCla
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := msgGenerator(requestBody)
 | 
			
		||||
		msg, err := msgGenerator(requestBody)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
@ -75,20 +83,23 @@ func postClaimHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostCla
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func usdxMintingVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingRewardVVesting(req.Sender, req.Receiver, req.MultiplierName)
 | 
			
		||||
func usdxMintingVVGenerator(req PostClaimVVestingReq) (sdk.Msg, error) {
 | 
			
		||||
	if len(req.DenomsToClaim) != 1 {
 | 
			
		||||
		return nil, fmt.Errorf("must only claim %s denom for usdx minting rewards, got '%s", types.USDXMintingRewardDenom, req.DenomsToClaim)
 | 
			
		||||
	}
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingRewardVVesting(req.Sender, req.Receiver, req.DenomsToClaim[0].MultiplierName), nil
 | 
			
		||||
}
 | 
			
		||||
func hardVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimHardRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func hardVVGenerator(req PostClaimVVestingReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimHardRewardVVesting(req.Sender, req.Receiver, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
func delegatorVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimDelegatorRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func delegatorVVGenerator(req PostClaimVVestingReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimDelegatorRewardVVesting(req.Sender, req.Receiver, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
func swapVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimSwapRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
func swapVVGenerator(req PostClaimVVestingReq) (sdk.Msg, error) {
 | 
			
		||||
	return types.NewMsgClaimSwapRewardVVesting(req.Sender, req.Receiver, req.DenomsToClaim...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimVVestingHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimVVestingReq) sdk.Msg) http.HandlerFunc {
 | 
			
		||||
func postClaimVVestingHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimVVestingReq) (sdk.Msg, error)) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimVVestingReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
@ -111,7 +122,11 @@ func postClaimVVestingHandlerFn(cliCtx context.CLIContext, msgGenerator func(req
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := msgGenerator(requestBody)
 | 
			
		||||
		msg, err := msgGenerator(requestBody)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
@ -65,8 +65,29 @@ func (suite *GenesisTestSuite) SetupTest() {
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "ukava", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "btcb/usdx", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.Multipliers{incentive.NewMultiplier(incentive.Small, 1, d("0.25")), incentive.NewMultiplier(incentive.Large, 12, d("1.0"))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "btcb/usdx", suite.genesisTime.Add(-1*oneYear), suite.genesisTime.Add(oneYear), cs(c("swp", 122354)))},
 | 
			
		||||
			incentive.MultipliersPerDenom{
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "ukava",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "hard",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
 | 
			
		||||
						incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "swp",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
 | 
			
		||||
						incentive.NewMultiplier(incentive.Medium, 6, d("0.8")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			suite.genesisTime.Add(5*oneYear),
 | 
			
		||||
		),
 | 
			
		||||
		incentive.DefaultGenesisRewardState,
 | 
			
		||||
@ -104,8 +125,29 @@ func (suite *GenesisTestSuite) TestExportedGenesisMatchesImported() {
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", genesisTime.Add(-1*oneYear), genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", genesisTime.Add(-1*oneYear), genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "ukava", genesisTime.Add(-1*oneYear), genesisTime.Add(oneYear), cs(c("hard", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "btcb/usdx", genesisTime.Add(-1*oneYear), genesisTime.Add(oneYear), cs(c("swap", 122354)))},
 | 
			
		||||
			incentive.Multipliers{incentive.NewMultiplier(incentive.Small, 1, d("0.25")), incentive.NewMultiplier(incentive.Large, 12, d("1.0"))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "btcb/usdx", genesisTime.Add(-1*oneYear), genesisTime.Add(oneYear), cs(c("swp", 122354)))},
 | 
			
		||||
			incentive.MultipliersPerDenom{
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "ukava",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "hard",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
 | 
			
		||||
						incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "swp",
 | 
			
		||||
					Multipliers: incentive.Multipliers{
 | 
			
		||||
						incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
 | 
			
		||||
						incentive.NewMultiplier(incentive.Medium, 6, d("0.8")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			genesisTime.Add(5*oneYear),
 | 
			
		||||
		),
 | 
			
		||||
		incentive.NewGenesisRewardState(
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimUSDXMintingReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimUSDXMintingReward) (*sdk.Result, error) {
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Sender, msg.MultiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -50,7 +50,7 @@ func handleMsgClaimUSDXMintingRewardVVesting(ctx sdk.Context, k keeper.Keeper, m
 | 
			
		||||
	if err := k.ValidateIsValidatorVestingAccount(ctx, msg.Sender); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Receiver, msg.MultiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -60,10 +60,12 @@ func handleMsgClaimUSDXMintingRewardVVesting(ctx sdk.Context, k keeper.Keeper, m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimHardReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimHardReward) (*sdk.Result, error) {
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimHardReward(ctx, msg.Sender, msg.Sender, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimHardReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
@ -75,9 +77,12 @@ func handleMsgClaimHardRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg type
 | 
			
		||||
	if err := k.ValidateIsValidatorVestingAccount(ctx, msg.Sender); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err := k.ClaimHardReward(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimHardReward(ctx, msg.Sender, msg.Receiver, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
@ -86,9 +91,11 @@ func handleMsgClaimHardRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg type
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimDelegatorReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimDelegatorReward) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimDelegatorReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimDelegatorReward(ctx, msg.Sender, msg.Sender, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
@ -100,9 +107,11 @@ func handleMsgClaimDelegatorRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg
 | 
			
		||||
	if err := k.ValidateIsValidatorVestingAccount(ctx, msg.Sender); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err := k.ClaimDelegatorReward(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimDelegatorReward(ctx, msg.Sender, msg.Receiver, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
@ -111,9 +120,11 @@ func handleMsgClaimDelegatorRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimSwapReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimSwapReward) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimSwapReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimSwapReward(ctx, msg.Sender, msg.Sender, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
@ -125,9 +136,11 @@ func handleMsgClaimSwapRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg type
 | 
			
		||||
	if err := k.ValidateIsValidatorVestingAccount(ctx, msg.Sender); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err := k.ClaimSwapReward(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	for _, selection := range msg.DenomsToClaim {
 | 
			
		||||
		err := k.ClaimSwapReward(ctx, msg.Sender, msg.Receiver, selection.Denom, selection.MultiplierName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaim() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaimMultiDenom() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
	receiverAddr := suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaim() {
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6)))
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -35,22 +35,24 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaim() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, receiverAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, receiverAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim a single denom
 | 
			
		||||
	// Claim denoms
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 2*7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(2*7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(2*7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: expectedRewards},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 2*7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.DelegatorRewardEquals(userAddr, nil)
 | 
			
		||||
@ -80,13 +82,13 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, suite.addrs[1], types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -95,14 +97,14 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 2*7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.DelegatorRewardEquals(userAddr, cs(c("hard", 2*7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVesting() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVestingMultiDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12)))
 | 
			
		||||
@ -112,7 +114,7 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVesting() {
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6)))
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -129,22 +131,24 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVesting() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("hard", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(2*7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(2*7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 2*7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
@ -178,13 +182,13 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVestingSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -193,7 +197,7 @@ func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVestingSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 2*7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaim() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaimMultiDenom() {
 | 
			
		||||
	userAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
@ -16,8 +16,8 @@ func (suite *HandlerTestSuite) TestPayoutHardClaim() {
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6)))
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -32,22 +32,24 @@ func (suite *HandlerTestSuite) TestPayoutHardClaim() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, receiverAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, receiverAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim a single denom
 | 
			
		||||
	// Claim denoms
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 2*7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(2*7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(2*7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.HardRewardEquals(userAddr, nil)
 | 
			
		||||
@ -76,13 +78,13 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, suite.addrs[1], "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, suite.addrs[1], types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -91,14 +93,14 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.HardRewardEquals(userAddr, cs(c("hard", 2*7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaimVVesting() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaimVVestingMultiDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("bnb", 1e12)))
 | 
			
		||||
@ -108,8 +110,8 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimVVesting() {
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6)))
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -124,22 +126,24 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimVVesting() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("hard", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(2*7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(2*7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
@ -172,13 +176,13 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimVVestingSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -187,7 +191,7 @@ func (suite *HandlerTestSuite) TestPayoutHardClaimVVestingSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
 | 
			
		||||
@ -11,26 +11,18 @@ import (
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
 | 
			
		||||
	hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist"
 | 
			
		||||
	validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const secondsPerDay = 24 * 60 * 60
 | 
			
		||||
 | 
			
		||||
// Test suite used for all keeper tests
 | 
			
		||||
type HandlerTestSuite struct {
 | 
			
		||||
	testutil.IntegrationTester
 | 
			
		||||
 | 
			
		||||
	// TODO remove these
 | 
			
		||||
	keeper     keeper.Keeper
 | 
			
		||||
	hardKeeper hardkeeper.Keeper
 | 
			
		||||
	cdpKeeper  cdpkeeper.Keeper
 | 
			
		||||
	handler    sdk.Handler
 | 
			
		||||
 | 
			
		||||
	genesisTime time.Time
 | 
			
		||||
	addrs       []sdk.AccAddress
 | 
			
		||||
}
 | 
			
		||||
@ -52,11 +44,6 @@ func (suite *HandlerTestSuite) SetupTest() {
 | 
			
		||||
func (suite *HandlerTestSuite) SetupApp() {
 | 
			
		||||
	suite.App = app.NewTestApp()
 | 
			
		||||
 | 
			
		||||
	suite.keeper = suite.App.GetIncentiveKeeper()
 | 
			
		||||
	suite.hardKeeper = suite.App.GetHardKeeper()
 | 
			
		||||
	suite.cdpKeeper = suite.App.GetCDPKeeper()
 | 
			
		||||
	suite.handler = incentive.NewHandler(suite.keeper)
 | 
			
		||||
 | 
			
		||||
	suite.Ctx = suite.App.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -106,13 +93,32 @@ func (suite *HandlerTestSuite) authBuilder() app.AuthGenesisBuilder {
 | 
			
		||||
func (suite *HandlerTestSuite) incentiveBuilder() testutil.IncentiveGenesisBuilder {
 | 
			
		||||
	return testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(suite.genesisTime).
 | 
			
		||||
		WithMultipliers(types.Multipliers{
 | 
			
		||||
			types.NewMultiplier(types.MultiplierName("small"), 1, d("0.2")),
 | 
			
		||||
			types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0")),
 | 
			
		||||
		WithMultipliers(types.MultipliersPerDenom{
 | 
			
		||||
			{
 | 
			
		||||
				Denom: "hard",
 | 
			
		||||
				Multipliers: types.Multipliers{
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("small"), 1, d("0.2")),
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0")),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Denom: "swap",
 | 
			
		||||
				Multipliers: types.Multipliers{
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("medium"), 6, d("0.5")),
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0")),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Denom: "ukava",
 | 
			
		||||
				Multipliers: types.Multipliers{
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("small"), 1, d("0.2")),
 | 
			
		||||
					types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0")),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaim() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaimMultiDenom() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
@ -134,22 +140,24 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaim() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("swap", 7*1e6), c("hard", 7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
@ -179,13 +187,13 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaimSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -194,14 +202,14 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaimSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.SwapRewardEquals(userAddr, cs(c("hard", 7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaimVVesting() {
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaimVVestingMultiDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12), c("busd", 1e12)))
 | 
			
		||||
@ -227,22 +235,24 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaimVVesting() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, types.NewSelection("hard", "small"), types.NewSelection("swap", "medium")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 7*1e6), c("swap", 7*1e6))
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
	expectedRewardsHard := c("hard", int64(0.2*float64(7*1e6)))
 | 
			
		||||
	expectedRewardsSwap := c("swap", int64(0.5*float64(7*1e6)))
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewardsHard, expectedRewardsSwap))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
		{Length: (17+31)*secondsPerDay - 7, Amount: cs(expectedRewardsHard)},
 | 
			
		||||
		{Length: (28 + 31 + 30 + 31 + 30) * secondsPerDay, Amount: cs(expectedRewardsSwap)}, // second length is stacked on top of the first
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
@ -275,13 +285,13 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaimVVestingSingleDenom() {
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, types.NewSelection("swap", "large")),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
@ -290,7 +300,7 @@ func (suite *HandlerTestSuite) TestPayoutSwapClaimVVestingSingleDenom() {
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ func (suite *HandlerTestSuite) TestPayoutUSDXClaim() {
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.USDXRewardEquals(userAddr, c(types.USDXMintingRewardDenom, 0))
 | 
			
		||||
@ -88,7 +88,7 @@ func (suite *HandlerTestSuite) TestPayoutUSDXClaimVVesting() {
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
		{Length: (17+31+28+31+30+31+30+31+31+30+31+30+31)*secondsPerDay - 7, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
 | 
			
		||||
@ -10,15 +10,20 @@ import (
 | 
			
		||||
 | 
			
		||||
// ClaimUSDXMintingReward pays out funds from a claim to a receiver account.
 | 
			
		||||
// Rewards are removed from a claim and paid out according to the multiplier, which reduces the reward amount in exchange for shorter vesting times.
 | 
			
		||||
func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName string) error {
 | 
			
		||||
	claim, found := k.GetUSDXMintingClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	name, err := types.ParseMultiplierName(multiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplierByDenom(ctx, types.USDXMintingRewardDenom, name)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, "denom '%s' has no multiplier '%s'", types.USDXMintingRewardDenom, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
@ -27,7 +32,7 @@ func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, owner, receiver sdk.AccA
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claim, err := k.SynchronizeUSDXMintingClaim(ctx, claim)
 | 
			
		||||
	claim, err = k.SynchronizeUSDXMintingClaim(ctx, claim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@ -62,15 +67,15 @@ func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, owner, receiver sdk.AccA
 | 
			
		||||
 | 
			
		||||
// ClaimHardReward pays out funds from a claim to a receiver account.
 | 
			
		||||
// Rewards are removed from a claim and paid out according to the multiplier, which reduces the reward amount in exchange for shorter vesting times.
 | 
			
		||||
func (k Keeper) ClaimHardReward(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName, denomsToClaim []string) error {
 | 
			
		||||
	_, found := k.GetHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
func (k Keeper) ClaimHardReward(ctx sdk.Context, owner, receiver sdk.AccAddress, denom string, multiplierName string) error {
 | 
			
		||||
	name, err := types.ParseMultiplierName(multiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	multiplier, found := k.GetMultiplierByDenom(ctx, denom, name)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, "denom '%s' has no multiplier '%s'", denom, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
@ -86,8 +91,10 @@ func (k Keeper) ClaimHardReward(ctx sdk.Context, owner, receiver sdk.AccAddress,
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	amt := syncedClaim.Reward.AmountOf(denom)
 | 
			
		||||
 | 
			
		||||
	claimingCoins := sdk.NewCoins(sdk.NewCoin(denom, amt))
 | 
			
		||||
	rewardCoins := sdk.NewCoins(sdk.NewCoin(denom, amt.ToDec().Mul(multiplier.Factor).RoundInt()))
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
@ -118,15 +125,20 @@ func (k Keeper) ClaimHardReward(ctx sdk.Context, owner, receiver sdk.AccAddress,
 | 
			
		||||
 | 
			
		||||
// ClaimDelegatorReward pays out funds from a claim to a receiver account.
 | 
			
		||||
// Rewards are removed from a claim and paid out according to the multiplier, which reduces the reward amount in exchange for shorter vesting times.
 | 
			
		||||
func (k Keeper) ClaimDelegatorReward(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName, denomsToClaim []string) error {
 | 
			
		||||
func (k Keeper) ClaimDelegatorReward(ctx sdk.Context, owner, receiver sdk.AccAddress, denom string, multiplierName string) error {
 | 
			
		||||
	claim, found := k.GetDelegatorClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	name, err := types.ParseMultiplierName(multiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplierByDenom(ctx, denom, name)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, "denom '%s' has no multiplier '%s'", denom, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
@ -140,8 +152,10 @@ func (k Keeper) ClaimDelegatorReward(ctx sdk.Context, owner, receiver sdk.AccAdd
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	amt := syncedClaim.Reward.AmountOf(denom)
 | 
			
		||||
 | 
			
		||||
	claimingCoins := sdk.NewCoins(sdk.NewCoin(denom, amt))
 | 
			
		||||
	rewardCoins := sdk.NewCoins(sdk.NewCoin(denom, amt.ToDec().Mul(multiplier.Factor).RoundInt()))
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
@ -173,15 +187,14 @@ func (k Keeper) ClaimDelegatorReward(ctx sdk.Context, owner, receiver sdk.AccAdd
 | 
			
		||||
 | 
			
		||||
// ClaimSwapReward pays out funds from a claim to a receiver account.
 | 
			
		||||
// Rewards are removed from a claim and paid out according to the multiplier, which reduces the reward amount in exchange for shorter vesting times.
 | 
			
		||||
func (k Keeper) ClaimSwapReward(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName, denomsToClaim []string) error {
 | 
			
		||||
	_, found := k.GetSwapClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
func (k Keeper) ClaimSwapReward(ctx sdk.Context, owner, receiver sdk.AccAddress, denom string, multiplierName string) error {
 | 
			
		||||
	name, err := types.ParseMultiplierName(multiplierName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	multiplier, found := k.GetMultiplierByDenom(ctx, denom, name)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, "denom '%s' has no multiplier '%s'", denom, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
@ -195,8 +208,10 @@ func (k Keeper) ClaimSwapReward(ctx sdk.Context, owner, receiver sdk.AccAddress,
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	amt := syncedClaim.Reward.AmountOf(denom)
 | 
			
		||||
 | 
			
		||||
	claimingCoins := sdk.NewCoins(sdk.NewCoin(denom, amt))
 | 
			
		||||
	rewardCoins := sdk.NewCoins(sdk.NewCoin(denom, amt.ToDec().Mul(multiplier.Factor).RoundInt()))
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
@ -236,4 +251,3 @@ func (k Keeper) ValidateIsValidatorVestingAccount(ctx sdk.Context, address sdk.A
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,13 @@ func (suite *ClaimTests) ErrorIs(err, target error) bool {
 | 
			
		||||
func (suite *ClaimTests) TestCannotClaimWhenMultiplierNotRecognised() {
 | 
			
		||||
	subspace := &fakeParamSubspace{
 | 
			
		||||
		params: types.Params{
 | 
			
		||||
			ClaimMultipliers: types.Multipliers{
 | 
			
		||||
				types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
			ClaimMultipliers: types.MultipliersPerDenom{
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "hard",
 | 
			
		||||
					Multipliers: types.Multipliers{
 | 
			
		||||
						types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@ -41,16 +46,11 @@ func (suite *ClaimTests) TestCannotClaimWhenMultiplierNotRecognised() {
 | 
			
		||||
	suite.storeDelegatorClaim(claim)
 | 
			
		||||
 | 
			
		||||
	// multiplier not in params
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, types.Large, nil)
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, "hard", "large")
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidMultiplier)
 | 
			
		||||
 | 
			
		||||
	// invalid multiplier name
 | 
			
		||||
	err = suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, "", nil)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidMultiplier)
 | 
			
		||||
 | 
			
		||||
	// invalid multiplier name
 | 
			
		||||
	const zeroWidthSpace = ""
 | 
			
		||||
	err = suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, types.Small+zeroWidthSpace, nil)
 | 
			
		||||
	err = suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, "hard", "")
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidMultiplier)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,8 +59,13 @@ func (suite *ClaimTests) TestCannotClaimAfterEndTime() {
 | 
			
		||||
 | 
			
		||||
	subspace := &fakeParamSubspace{
 | 
			
		||||
		params: types.Params{
 | 
			
		||||
			ClaimMultipliers: types.Multipliers{
 | 
			
		||||
				types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
			ClaimMultipliers: types.MultipliersPerDenom{
 | 
			
		||||
				{
 | 
			
		||||
					Denom: "hard",
 | 
			
		||||
					Multipliers: types.Multipliers{
 | 
			
		||||
						types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			ClaimEnd: endTime,
 | 
			
		||||
		},
 | 
			
		||||
@ -76,6 +81,6 @@ func (suite *ClaimTests) TestCannotClaimAfterEndTime() {
 | 
			
		||||
	}
 | 
			
		||||
	suite.storeDelegatorClaim(claim)
 | 
			
		||||
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, types.Small, nil)
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, "hard", "small")
 | 
			
		||||
	suite.ErrorIs(err, types.ErrClaimExpired)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -64,12 +64,14 @@ func (k Keeper) GetDelegatorRewardPeriods(ctx sdk.Context, denom string) (types.
 | 
			
		||||
	return types.MultiRewardPeriod{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMultiplier returns the multiplier with the specified name if it's found in the params
 | 
			
		||||
func (k Keeper) GetMultiplier(ctx sdk.Context, name types.MultiplierName) (types.Multiplier, bool) {
 | 
			
		||||
// GetMultiplierByDenom fetches a multiplier from the params matching the denom and name.
 | 
			
		||||
func (k Keeper) GetMultiplierByDenom(ctx sdk.Context, denom string, name types.MultiplierName) (types.Multiplier, bool) {
 | 
			
		||||
	params := k.GetParams(ctx)
 | 
			
		||||
	for _, m := range params.ClaimMultipliers {
 | 
			
		||||
		if m.Name == name {
 | 
			
		||||
			return m, true
 | 
			
		||||
 | 
			
		||||
	for _, dm := range params.ClaimMultipliers {
 | 
			
		||||
		if dm.Denom == denom {
 | 
			
		||||
			m, found := dm.Multipliers.Get(name)
 | 
			
		||||
			return m, found
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return types.Multiplier{}, false
 | 
			
		||||
 | 
			
		||||
@ -502,7 +502,7 @@ func (suite *PayoutTestSuite) TestGetPeriodLength() {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "exactly half of month",
 | 
			
		||||
			name: "exactly half of month, is pushed to start of month + lockup",
 | 
			
		||||
			args: args{
 | 
			
		||||
				blockTime:      time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				multiplier:     types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
 | 
			
		||||
@ -525,6 +525,18 @@ func (suite *PayoutTestSuite) TestGetPeriodLength() {
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "just after start of month payout time, is pushed to mid month + lockup",
 | 
			
		||||
			args: args{
 | 
			
		||||
				blockTime:      time.Date(2020, 12, 1, 14, 0, 1, 0, time.UTC),
 | 
			
		||||
				multiplier:     types.NewMultiplier(types.Medium, 1, sdk.MustNewDecFromStr("0.333333")),
 | 
			
		||||
				expectedLength: time.Date(2021, 1, 15, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 12, 1, 14, 0, 1, 0, time.UTC).Unix(),
 | 
			
		||||
			},
 | 
			
		||||
			errArgs: errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
 | 
			
		||||
@ -222,7 +222,6 @@ func (suite *SupplyRewardsTestSuite) TestAccumulateHardSupplyRewards() {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// TODO test accumulate when there is a reward period with 0 rewardsPerSecond
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
 | 
			
		||||
@ -166,7 +166,7 @@ func (builder IncentiveGenesisBuilder) WithSimpleUSDXRewardPeriod(ctype string,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithMultipliers(multipliers types.Multipliers) IncentiveGenesisBuilder {
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithMultipliers(multipliers types.MultipliersPerDenom) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.ClaimMultipliers = multipliers
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@ var (
 | 
			
		||||
	ErrZeroClaim                     = sdkerrors.Register(ModuleName, 9, "cannot claim - claim amount rounds to zero")
 | 
			
		||||
	ErrClaimExpired                  = sdkerrors.Register(ModuleName, 10, "claim has expired")
 | 
			
		||||
	ErrInvalidClaimType              = sdkerrors.Register(ModuleName, 11, "invalid claim type")
 | 
			
		||||
	ErrInvalidClaimOwner             = sdkerrors.Register(ModuleName, 12, "invalid claim owner")
 | 
			
		||||
	ErrDecreasingRewardFactor        = sdkerrors.Register(ModuleName, 13, "found new reward factor less than an old reward factor")
 | 
			
		||||
	ErrInvalidClaimDenoms            = sdkerrors.Register(ModuleName, 14, "invalid claim denoms")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -45,8 +45,14 @@ func TestGenesisState_Validate(t *testing.T) {
 | 
			
		||||
					DefaultMultiRewardPeriods,
 | 
			
		||||
					DefaultMultiRewardPeriods,
 | 
			
		||||
					DefaultMultiRewardPeriods,
 | 
			
		||||
					Multipliers{
 | 
			
		||||
						NewMultiplier(Small, 1, sdk.MustNewDecFromStr("0.33")),
 | 
			
		||||
					MultipliersPerDenom{
 | 
			
		||||
						{
 | 
			
		||||
							Denom: "ukava",
 | 
			
		||||
							Multipliers: Multipliers{
 | 
			
		||||
								NewMultiplier(Small, 1, sdk.MustNewDecFromStr("0.33")),
 | 
			
		||||
								NewMultiplier(Large, 12, sdk.MustNewDecFromStr("1.00")),
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				),
 | 
			
		||||
 | 
			
		||||
@ -110,17 +110,15 @@ func (msg MsgClaimUSDXMintingRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
 | 
			
		||||
// MsgClaimHardReward message type used to claim Hard liquidity provider rewards
 | 
			
		||||
type MsgClaimHardReward struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	Sender        sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	DenomsToClaim Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimHardReward returns a new MsgClaimHardReward.
 | 
			
		||||
func NewMsgClaimHardReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimHardReward {
 | 
			
		||||
func NewMsgClaimHardReward(sender sdk.AccAddress, denomsToClaim ...Selection) MsgClaimHardReward {
 | 
			
		||||
	return MsgClaimHardReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -137,17 +135,9 @@ func (msg MsgClaimHardReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -164,19 +154,17 @@ func (msg MsgClaimHardReward) GetSigners() []sdk.AccAddress {
 | 
			
		||||
 | 
			
		||||
// MsgClaimHardRewardVVesting message type used to claim Hard liquidity provider rewards for validator vesting accounts
 | 
			
		||||
type MsgClaimHardRewardVVesting struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver       sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	Sender        sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver      sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	DenomsToClaim Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimHardRewardVVesting returns a new MsgClaimHardRewardVVesting.
 | 
			
		||||
func NewMsgClaimHardRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimHardRewardVVesting {
 | 
			
		||||
func NewMsgClaimHardRewardVVesting(sender, receiver sdk.AccAddress, denomsToClaim ...Selection) MsgClaimHardRewardVVesting {
 | 
			
		||||
	return MsgClaimHardRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		Receiver:      receiver,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -196,17 +184,9 @@ func (msg MsgClaimHardRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -223,17 +203,15 @@ func (msg MsgClaimHardRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
 | 
			
		||||
// MsgClaimDelegatorReward message type used to claim delegator rewards
 | 
			
		||||
type MsgClaimDelegatorReward struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	Sender        sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	DenomsToClaim Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimDelegatorReward returns a new MsgClaimDelegatorReward.
 | 
			
		||||
func NewMsgClaimDelegatorReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimDelegatorReward {
 | 
			
		||||
func NewMsgClaimDelegatorReward(sender sdk.AccAddress, denomsToClaim ...Selection) MsgClaimDelegatorReward {
 | 
			
		||||
	return MsgClaimDelegatorReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -250,17 +228,9 @@ func (msg MsgClaimDelegatorReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -277,19 +247,17 @@ func (msg MsgClaimDelegatorReward) GetSigners() []sdk.AccAddress {
 | 
			
		||||
 | 
			
		||||
// MsgClaimDelegatorRewardVVesting message type used to claim delegator rewards for validator vesting accounts
 | 
			
		||||
type MsgClaimDelegatorRewardVVesting struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver       sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	Sender        sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver      sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	DenomsToClaim Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgClaimDelegatorRewardVVesting returns a new MsgClaimDelegatorRewardVVesting.
 | 
			
		||||
func NewMsgClaimDelegatorRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimDelegatorRewardVVesting {
 | 
			
		||||
func NewMsgClaimDelegatorRewardVVesting(sender, receiver sdk.AccAddress, denomsToClaim ...Selection) MsgClaimDelegatorRewardVVesting {
 | 
			
		||||
	return MsgClaimDelegatorRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		Receiver:      receiver,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -309,17 +277,9 @@ func (msg MsgClaimDelegatorRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -338,15 +298,14 @@ func (msg MsgClaimDelegatorRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
type MsgClaimSwapReward struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	DenomsToClaim  Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimSwapReward returns a new MsgClaimSwapReward.
 | 
			
		||||
func NewMsgClaimSwapReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimSwapReward {
 | 
			
		||||
func NewMsgClaimSwapReward(sender sdk.AccAddress, denomsToClaim ...Selection) MsgClaimSwapReward {
 | 
			
		||||
	return MsgClaimSwapReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -363,17 +322,9 @@ func (msg MsgClaimSwapReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -390,19 +341,17 @@ func (msg MsgClaimSwapReward) GetSigners() []sdk.AccAddress {
 | 
			
		||||
 | 
			
		||||
// MsgClaimSwapRewardVVesting message type used to claim delegator rewards for validator vesting accounts
 | 
			
		||||
type MsgClaimSwapRewardVVesting struct {
 | 
			
		||||
	Sender         sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver       sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	MultiplierName string         `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
	DenomsToClaim  []string       `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
	Sender        sdk.AccAddress `json:"sender" yaml:"sender"`
 | 
			
		||||
	Receiver      sdk.AccAddress `json:"receiver" yaml:"receiver"`
 | 
			
		||||
	DenomsToClaim Selections     `json:"denoms_to_claim" yaml:"denoms_to_claim"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgClaimSwapRewardVVesting returns a new MsgClaimSwapRewardVVesting.
 | 
			
		||||
func NewMsgClaimSwapRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimSwapRewardVVesting {
 | 
			
		||||
func NewMsgClaimSwapRewardVVesting(sender, receiver sdk.AccAddress, denomsToClaim ...Selection) MsgClaimSwapRewardVVesting {
 | 
			
		||||
	return MsgClaimSwapRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
		Sender:        sender,
 | 
			
		||||
		Receiver:      receiver,
 | 
			
		||||
		DenomsToClaim: denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -422,17 +371,9 @@ func (msg MsgClaimSwapRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
	if err := msg.DenomsToClaim.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for i, d := range msg.DenomsToClaim {
 | 
			
		||||
		if i >= MaxDenomsToClaim {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
		}
 | 
			
		||||
		if err := sdk.ValidateDenom(d); err != nil {
 | 
			
		||||
			return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,10 +21,9 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		receiver       sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
		denomsToClaim  []string
 | 
			
		||||
		sender        sdk.AccAddress
 | 
			
		||||
		receiver      sdk.AccAddress
 | 
			
		||||
		denomsToClaim types.Selections
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
@ -34,9 +33,14 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
@ -45,9 +49,14 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
@ -56,54 +65,30 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "small",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty denoms to claim is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid sender",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         sdk.AccAddress{},
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid receiver",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       sdk.AccAddress{},
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid multiplier",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "huge",
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "huge",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
@ -112,21 +97,85 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "Large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty denoms to claim is not valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				receiver:      validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nil denoms to claim is not valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				receiver:      validAddress,
 | 
			
		||||
				denomsToClaim: nil,
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid sender",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:   sdk.AccAddress{},
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid receiver",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: sdk.AccAddress{},
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid claim denom",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{"a denom string that is invalid because it is much too long"},
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "a denom string that is invalid because it is much too long",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
@ -135,10 +184,29 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "too many claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  tooManyClaimDenoms(),
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				receiver:      validAddress,
 | 
			
		||||
				denomsToClaim: tooManySelections(),
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "duplicated claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:   validAddress,
 | 
			
		||||
				receiver: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
@ -149,13 +217,13 @@ func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		msgs := []sdk.Msg{
 | 
			
		||||
			types.NewMsgClaimHardRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.denomsToClaim...,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimDelegatorRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.denomsToClaim...,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimSwapRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.denomsToClaim...,
 | 
			
		||||
			),
 | 
			
		||||
		}
 | 
			
		||||
		for _, msg := range msgs {
 | 
			
		||||
@ -180,20 +248,25 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
		denomsToClaim  []string
 | 
			
		||||
		sender        sdk.AccAddress
 | 
			
		||||
		denomsToClaim types.Selections
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		msgArgs msgArgs
 | 
			
		||||
		expect  expectedErr
 | 
			
		||||
	}{
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
@ -202,8 +275,13 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
@ -212,39 +290,28 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "small",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty denoms to claim is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid sender",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         sdk.AccAddress{},
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid multiplier",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "huge",
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "huge",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
@ -253,19 +320,63 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "Large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty denoms to claim is not valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nil denoms to claim is not valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				denomsToClaim: nil,
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid sender",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender: sdk.AccAddress{},
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: sdkerrors.ErrInvalidAddress,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid claim denom",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{"a denom string that is invalid because it is much too long"},
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "a denom string that is invalid because it is much too long",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
@ -274,9 +385,27 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			name: "too many claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  tooManyClaimDenoms(),
 | 
			
		||||
				sender:        validAddress,
 | 
			
		||||
				denomsToClaim: tooManySelections(),
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "duplicated claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender: validAddress,
 | 
			
		||||
				denomsToClaim: types.Selections{
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "medium",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Denom:          "hard",
 | 
			
		||||
						MultiplierName: "large",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
@ -286,15 +415,9 @@ func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		msgs := []sdk.Msg{
 | 
			
		||||
			types.NewMsgClaimHardReward(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimDelegatorReward(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimSwapReward(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimHardReward(tc.msgArgs.sender, tc.msgArgs.denomsToClaim...),
 | 
			
		||||
			types.NewMsgClaimDelegatorReward(tc.msgArgs.sender, tc.msgArgs.denomsToClaim...),
 | 
			
		||||
			types.NewMsgClaimSwapReward(tc.msgArgs.sender, tc.msgArgs.denomsToClaim...),
 | 
			
		||||
		}
 | 
			
		||||
		for _, msg := range msgs {
 | 
			
		||||
			t.Run(msg.Type()+" "+tc.name, func(t *testing.T) {
 | 
			
		||||
@ -520,3 +643,14 @@ func tooManyClaimDenoms() []string {
 | 
			
		||||
	}
 | 
			
		||||
	return claimDenoms
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tooManySelections() types.Selections {
 | 
			
		||||
	selections := make(types.Selections, types.MaxDenomsToClaim+1)
 | 
			
		||||
	for i := range selections {
 | 
			
		||||
		selections[i] = types.Selection{
 | 
			
		||||
			Denom:          fmt.Sprintf("denom%d", i),
 | 
			
		||||
			MultiplierName: "large",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return selections
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								x/incentive/types/multipliers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								x/incentive/types/multipliers.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Available reward multipliers names
 | 
			
		||||
const (
 | 
			
		||||
	Small  MultiplierName = "small"
 | 
			
		||||
	Medium MultiplierName = "medium"
 | 
			
		||||
	Large  MultiplierName = "large"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MultiplierName is the user facing ID for a multiplier. There is a restricted set of possible values.
 | 
			
		||||
type MultiplierName string
 | 
			
		||||
 | 
			
		||||
// IsValid checks if the input is one of the expected strings
 | 
			
		||||
func (mn MultiplierName) IsValid() error {
 | 
			
		||||
	switch mn {
 | 
			
		||||
	case Small, Medium, Large:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return sdkerrors.Wrapf(ErrInvalidMultiplier, "invalid multiplier name: %s", mn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseMultiplierName converts a string into a valid MultiplierName value.
 | 
			
		||||
func ParseMultiplierName(unparsedName string) (MultiplierName, error) {
 | 
			
		||||
	name := MultiplierName(unparsedName)
 | 
			
		||||
	if err := name.IsValid(); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multiplier amount the claim rewards get increased by, along with how long the claim rewards are locked
 | 
			
		||||
type Multiplier struct {
 | 
			
		||||
	Name         MultiplierName `json:"name" yaml:"name"`
 | 
			
		||||
	MonthsLockup int64          `json:"months_lockup" yaml:"months_lockup"`
 | 
			
		||||
	Factor       sdk.Dec        `json:"factor" yaml:"factor"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMultiplier returns a new Multiplier
 | 
			
		||||
func NewMultiplier(name MultiplierName, lockup int64, factor sdk.Dec) Multiplier {
 | 
			
		||||
	return Multiplier{
 | 
			
		||||
		Name:         name,
 | 
			
		||||
		MonthsLockup: lockup,
 | 
			
		||||
		Factor:       factor,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate multiplier param
 | 
			
		||||
func (m Multiplier) Validate() error {
 | 
			
		||||
	if err := m.Name.IsValid(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if m.MonthsLockup < 0 {
 | 
			
		||||
		return fmt.Errorf("expected non-negative lockup, got %d", m.MonthsLockup)
 | 
			
		||||
	}
 | 
			
		||||
	if m.Factor.IsNegative() {
 | 
			
		||||
		return fmt.Errorf("expected non-negative factor, got %s", m.Factor.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (m Multiplier) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Claim Multiplier:
 | 
			
		||||
	Name: %s
 | 
			
		||||
	Months Lockup %d
 | 
			
		||||
	Factor %s
 | 
			
		||||
	`, m.Name, m.MonthsLockup, m.Factor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multipliers is a slice of Multiplier
 | 
			
		||||
type Multipliers []Multiplier
 | 
			
		||||
 | 
			
		||||
// Validate validates each multiplier
 | 
			
		||||
func (ms Multipliers) Validate() error {
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		if err := m.Validate(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a multiplier with a matching name
 | 
			
		||||
func (ms Multipliers) Get(name MultiplierName) (Multiplier, bool) {
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		if m.Name == name {
 | 
			
		||||
			return m, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return Multiplier{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (ms Multipliers) String() string {
 | 
			
		||||
	out := "Claim Multipliers\n"
 | 
			
		||||
	for _, s := range ms {
 | 
			
		||||
		out += fmt.Sprintf("%s\n", s)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultipliersPerDenom is a map of denoms to a set of multipliers
 | 
			
		||||
type MultipliersPerDenom []struct {
 | 
			
		||||
	Denom       string      `json:"denom" yaml:"denom"`
 | 
			
		||||
	Multipliers Multipliers `json:"multipliers" yaml:"multipliers"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate checks each denom and multipliers for invalid values.
 | 
			
		||||
func (mpd MultipliersPerDenom) Validate() error {
 | 
			
		||||
	foundDenoms := map[string]bool{}
 | 
			
		||||
 | 
			
		||||
	for _, item := range mpd {
 | 
			
		||||
		if err := sdk.ValidateDenom(item.Denom); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := item.Multipliers.Validate(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if foundDenoms[item.Denom] {
 | 
			
		||||
			return fmt.Errorf("")
 | 
			
		||||
		}
 | 
			
		||||
		foundDenoms[item.Denom] = true
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Selection a pair of denom and multiplier name. It holds the choice of multiplier a user makes when they claim a denom.
 | 
			
		||||
type Selection struct {
 | 
			
		||||
	Denom          string `json:"denom" yaml:"denom"`
 | 
			
		||||
	MultiplierName string `json:"multiplier_name" yaml:"multiplier_name"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSelection returns a new Selection
 | 
			
		||||
func NewSelection(denom, multiplierName string) Selection {
 | 
			
		||||
	return Selection{
 | 
			
		||||
		Denom:          denom,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate performs basic validation checks
 | 
			
		||||
func (s Selection) Validate() error {
 | 
			
		||||
	if err := sdk.ValidateDenom(s.Denom); err != nil {
 | 
			
		||||
		return sdkerrors.Wrap(ErrInvalidClaimDenoms, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := ParseMultiplierName(s.MultiplierName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Selections are a list of denom - multiplier pairs that specify what rewards to claim and with what lockups.
 | 
			
		||||
type Selections []Selection
 | 
			
		||||
 | 
			
		||||
// NewSelectionsFromMap creates a new set of selections from a string to string map.
 | 
			
		||||
// It sorts the output before returning.
 | 
			
		||||
func NewSelectionsFromMap(selectionMap map[string]string) Selections {
 | 
			
		||||
	var selections Selections
 | 
			
		||||
	for k, v := range selectionMap {
 | 
			
		||||
		selections = append(selections, NewSelection(k, v))
 | 
			
		||||
	}
 | 
			
		||||
	// deterministically sort the slice to protect against the random range order causing consensus failures
 | 
			
		||||
	sort.Slice(selections, func(i, j int) bool {
 | 
			
		||||
		if selections[i].Denom != selections[j].Denom {
 | 
			
		||||
			return selections[i].Denom < selections[j].Denom
 | 
			
		||||
		}
 | 
			
		||||
		return selections[i].MultiplierName < selections[j].MultiplierName
 | 
			
		||||
	})
 | 
			
		||||
	return selections
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Valdate performs basic validaton checks
 | 
			
		||||
func (ss Selections) Validate() error {
 | 
			
		||||
	if len(ss) == 0 {
 | 
			
		||||
		return sdkerrors.Wrap(ErrInvalidClaimDenoms, "cannot claim 0 denoms")
 | 
			
		||||
	}
 | 
			
		||||
	if len(ss) >= MaxDenomsToClaim {
 | 
			
		||||
		return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim more than %d denoms", MaxDenomsToClaim)
 | 
			
		||||
	}
 | 
			
		||||
	foundDenoms := map[string]bool{}
 | 
			
		||||
	for _, s := range ss {
 | 
			
		||||
		if err := s.Validate(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if foundDenoms[s.Denom] {
 | 
			
		||||
			return sdkerrors.Wrapf(ErrInvalidClaimDenoms, "cannot claim denom '%s' more than once", s.Denom)
 | 
			
		||||
		}
 | 
			
		||||
		foundDenoms[s.Denom] = true
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -7,20 +7,12 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/params"
 | 
			
		||||
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
			
		||||
 | 
			
		||||
	kavadistTypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Valid reward multipliers
 | 
			
		||||
const (
 | 
			
		||||
	Small  MultiplierName = "small"
 | 
			
		||||
	Medium MultiplierName = "medium"
 | 
			
		||||
	Large  MultiplierName = "large"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Parameter keys and default values
 | 
			
		||||
var (
 | 
			
		||||
	KeyUSDXMintingRewardPeriods = []byte("USDXMintingRewardPeriods")
 | 
			
		||||
@ -34,7 +26,7 @@ var (
 | 
			
		||||
	DefaultActive             = false
 | 
			
		||||
	DefaultRewardPeriods      = RewardPeriods{}
 | 
			
		||||
	DefaultMultiRewardPeriods = MultiRewardPeriods{}
 | 
			
		||||
	DefaultMultipliers        = Multipliers{}
 | 
			
		||||
	DefaultMultipliers        = MultipliersPerDenom{}
 | 
			
		||||
	DefaultClaimEnd           = tmtime.Canonical(time.Unix(1, 0))
 | 
			
		||||
 | 
			
		||||
	BondDenom              = "ukava"
 | 
			
		||||
@ -45,18 +37,18 @@ var (
 | 
			
		||||
 | 
			
		||||
// Params governance parameters for the incentive module
 | 
			
		||||
type Params struct {
 | 
			
		||||
	USDXMintingRewardPeriods RewardPeriods      `json:"usdx_minting_reward_periods" yaml:"usdx_minting_reward_periods"`
 | 
			
		||||
	HardSupplyRewardPeriods  MultiRewardPeriods `json:"hard_supply_reward_periods" yaml:"hard_supply_reward_periods"`
 | 
			
		||||
	HardBorrowRewardPeriods  MultiRewardPeriods `json:"hard_borrow_reward_periods" yaml:"hard_borrow_reward_periods"`
 | 
			
		||||
	DelegatorRewardPeriods   MultiRewardPeriods `json:"delegator_reward_periods" yaml:"delegator_reward_periods"`
 | 
			
		||||
	SwapRewardPeriods        MultiRewardPeriods `json:"swap_reward_periods" yaml:"swap_reward_periods"`
 | 
			
		||||
	ClaimMultipliers         Multipliers        `json:"claim_multipliers" yaml:"claim_multipliers"`
 | 
			
		||||
	ClaimEnd                 time.Time          `json:"claim_end" yaml:"claim_end"`
 | 
			
		||||
	USDXMintingRewardPeriods RewardPeriods       `json:"usdx_minting_reward_periods" yaml:"usdx_minting_reward_periods"`
 | 
			
		||||
	HardSupplyRewardPeriods  MultiRewardPeriods  `json:"hard_supply_reward_periods" yaml:"hard_supply_reward_periods"`
 | 
			
		||||
	HardBorrowRewardPeriods  MultiRewardPeriods  `json:"hard_borrow_reward_periods" yaml:"hard_borrow_reward_periods"`
 | 
			
		||||
	DelegatorRewardPeriods   MultiRewardPeriods  `json:"delegator_reward_periods" yaml:"delegator_reward_periods"`
 | 
			
		||||
	SwapRewardPeriods        MultiRewardPeriods  `json:"swap_reward_periods" yaml:"swap_reward_periods"`
 | 
			
		||||
	ClaimMultipliers         MultipliersPerDenom `json:"claim_multipliers" yaml:"claim_multipliers"`
 | 
			
		||||
	ClaimEnd                 time.Time           `json:"claim_end" yaml:"claim_end"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewParams returns a new params object
 | 
			
		||||
func NewParams(usdxMinting RewardPeriods, hardSupply, hardBorrow, delegator, swap MultiRewardPeriods,
 | 
			
		||||
	multipliers Multipliers, claimEnd time.Time) Params {
 | 
			
		||||
	multipliers MultipliersPerDenom, claimEnd time.Time) Params {
 | 
			
		||||
	return Params{
 | 
			
		||||
		USDXMintingRewardPeriods: usdxMinting,
 | 
			
		||||
		HardSupplyRewardPeriods:  hardSupply,
 | 
			
		||||
@ -89,7 +81,7 @@ func (p Params) String() string {
 | 
			
		||||
	Hard Borrow Reward Periods: %s
 | 
			
		||||
	Delegator Reward Periods: %s
 | 
			
		||||
	Swap Reward Periods: %s
 | 
			
		||||
	Claim Multipliers :%s
 | 
			
		||||
	Claim Multipliers: %s
 | 
			
		||||
	Claim End Time: %s
 | 
			
		||||
	`, p.USDXMintingRewardPeriods, p.HardSupplyRewardPeriods, p.HardBorrowRewardPeriods,
 | 
			
		||||
		p.DelegatorRewardPeriods, p.SwapRewardPeriods, p.ClaimMultipliers, p.ClaimEnd)
 | 
			
		||||
@ -108,15 +100,15 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
 | 
			
		||||
		params.NewParamSetPair(KeyHardBorrowRewardPeriods, &p.HardBorrowRewardPeriods, validateMultiRewardPeriodsParam),
 | 
			
		||||
		params.NewParamSetPair(KeyDelegatorRewardPeriods, &p.DelegatorRewardPeriods, validateMultiRewardPeriodsParam),
 | 
			
		||||
		params.NewParamSetPair(KeySwapRewardPeriods, &p.SwapRewardPeriods, validateMultiRewardPeriodsParam),
 | 
			
		||||
		params.NewParamSetPair(KeyMultipliers, &p.ClaimMultipliers, validateMultipliersPerDenomParam),
 | 
			
		||||
		params.NewParamSetPair(KeyClaimEnd, &p.ClaimEnd, validateClaimEndParam),
 | 
			
		||||
		params.NewParamSetPair(KeyMultipliers, &p.ClaimMultipliers, validateMultipliersParam),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate checks that the parameters have valid values.
 | 
			
		||||
func (p Params) Validate() error {
 | 
			
		||||
 | 
			
		||||
	if err := validateMultipliersParam(p.ClaimMultipliers); err != nil {
 | 
			
		||||
	if err := validateMultipliersPerDenomParam(p.ClaimMultipliers); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -169,6 +161,14 @@ func validateMultipliersParam(i interface{}) error {
 | 
			
		||||
	return multipliers.Validate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateMultipliersPerDenomParam(i interface{}) error {
 | 
			
		||||
	multipliers, ok := i.(MultipliersPerDenom)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("invalid parameter type: %T", i)
 | 
			
		||||
	}
 | 
			
		||||
	return multipliers.Validate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateClaimEndParam(i interface{}) error {
 | 
			
		||||
	endTime, ok := i.(time.Time)
 | 
			
		||||
	if !ok {
 | 
			
		||||
@ -360,77 +360,3 @@ func (mrps MultiRewardPeriods) Validate() error {
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multiplier amount the claim rewards get increased by, along with how long the claim rewards are locked
 | 
			
		||||
type Multiplier struct {
 | 
			
		||||
	Name         MultiplierName `json:"name" yaml:"name"`
 | 
			
		||||
	MonthsLockup int64          `json:"months_lockup" yaml:"months_lockup"`
 | 
			
		||||
	Factor       sdk.Dec        `json:"factor" yaml:"factor"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMultiplier returns a new Multiplier
 | 
			
		||||
func NewMultiplier(name MultiplierName, lockup int64, factor sdk.Dec) Multiplier {
 | 
			
		||||
	return Multiplier{
 | 
			
		||||
		Name:         name,
 | 
			
		||||
		MonthsLockup: lockup,
 | 
			
		||||
		Factor:       factor,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate multiplier param
 | 
			
		||||
func (m Multiplier) Validate() error {
 | 
			
		||||
	if err := m.Name.IsValid(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if m.MonthsLockup < 0 {
 | 
			
		||||
		return fmt.Errorf("expected non-negative lockup, got %d", m.MonthsLockup)
 | 
			
		||||
	}
 | 
			
		||||
	if m.Factor.IsNegative() {
 | 
			
		||||
		return fmt.Errorf("expected non-negative factor, got %s", m.Factor.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (m Multiplier) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Claim Multiplier:
 | 
			
		||||
	Name: %s
 | 
			
		||||
	Months Lockup %d
 | 
			
		||||
	Factor %s
 | 
			
		||||
	`, m.Name, m.MonthsLockup, m.Factor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multipliers slice of Multiplier
 | 
			
		||||
type Multipliers []Multiplier
 | 
			
		||||
 | 
			
		||||
// Validate validates each multiplier
 | 
			
		||||
func (ms Multipliers) Validate() error {
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		if err := m.Validate(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (ms Multipliers) String() string {
 | 
			
		||||
	out := "Claim Multipliers\n"
 | 
			
		||||
	for _, s := range ms {
 | 
			
		||||
		out += fmt.Sprintf("%s\n", s)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiplierName name for valid multiplier
 | 
			
		||||
type MultiplierName string
 | 
			
		||||
 | 
			
		||||
// IsValid checks if the input is one of the expected strings
 | 
			
		||||
func (mn MultiplierName) IsValid() error {
 | 
			
		||||
	switch mn {
 | 
			
		||||
	case Small, Medium, Large:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return sdkerrors.Wrapf(ErrInvalidMultiplier, "invalid multiplier name: %s", mn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -77,19 +77,27 @@ func (suite *ParamTestSuite) TestParamValidation() {
 | 
			
		||||
						time.Date(2024, 10, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
						sdk.NewCoin(types.USDXMintingRewardDenom, sdk.NewInt(122354)),
 | 
			
		||||
					)},
 | 
			
		||||
				ClaimMultipliers: types.Multipliers{
 | 
			
		||||
					types.NewMultiplier(
 | 
			
		||||
						types.Small, 1, sdk.MustNewDecFromStr("0.25"),
 | 
			
		||||
					),
 | 
			
		||||
					types.NewMultiplier(
 | 
			
		||||
						types.Large, 1, sdk.MustNewDecFromStr("1.0"),
 | 
			
		||||
					),
 | 
			
		||||
				},
 | 
			
		||||
				HardSupplyRewardPeriods: types.DefaultMultiRewardPeriods,
 | 
			
		||||
				HardBorrowRewardPeriods: types.DefaultMultiRewardPeriods,
 | 
			
		||||
				DelegatorRewardPeriods:  types.DefaultMultiRewardPeriods,
 | 
			
		||||
				SwapRewardPeriods:       types.DefaultMultiRewardPeriods,
 | 
			
		||||
				ClaimEnd:                time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
				ClaimMultipliers: types.MultipliersPerDenom{
 | 
			
		||||
					{
 | 
			
		||||
						Denom: "hard",
 | 
			
		||||
						Multipliers: types.Multipliers{
 | 
			
		||||
							types.NewMultiplier(types.Small, 1, sdk.MustNewDecFromStr("0.25")),
 | 
			
		||||
							types.NewMultiplier(types.Large, 12, sdk.MustNewDecFromStr("1.0")),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Denom: "ukava",
 | 
			
		||||
						Multipliers: types.Multipliers{
 | 
			
		||||
							types.NewMultiplier(types.Small, 1, sdk.MustNewDecFromStr("0.2")),
 | 
			
		||||
							types.NewMultiplier(types.Large, 12, sdk.MustNewDecFromStr("1.0")),
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				ClaimEnd: time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
 | 
			
		||||
@ -18,29 +18,3 @@ func GetTotalVestingPeriodLength(periods vesting.Periods) int64 {
 | 
			
		||||
	}
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiplyCoins multiplies each value in a set of coins by a single decimal value, rounding the result.
 | 
			
		||||
func MultiplyCoins(coins sdk.Coins, multiple sdk.Dec) sdk.Coins {
 | 
			
		||||
	var result sdk.Coins
 | 
			
		||||
	for _, coin := range coins {
 | 
			
		||||
		result = result.Add(
 | 
			
		||||
			sdk.NewCoin(coin.Denom, coin.Amount.ToDec().Mul(multiple).RoundInt()),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterCoins returns a subset of the coins by denom. Specifying no denoms will return the original coins.
 | 
			
		||||
func FilterCoins(coins sdk.Coins, denoms []string) sdk.Coins {
 | 
			
		||||
 | 
			
		||||
	if len(denoms) == 0 {
 | 
			
		||||
		// with no filter, return all the coins
 | 
			
		||||
		return coins
 | 
			
		||||
	}
 | 
			
		||||
	// otherwise select denoms in filter
 | 
			
		||||
	var filteredCoins sdk.Coins
 | 
			
		||||
	for _, denom := range denoms {
 | 
			
		||||
		filteredCoins = filteredCoins.Add(sdk.NewCoin(denom, coins.AmountOf(denom)))
 | 
			
		||||
	}
 | 
			
		||||
	return filteredCoins
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
@ -43,128 +42,3 @@ func TestGetTotalVestingPeriodLength(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMultiplyCoins(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		coins    sdk.Coins
 | 
			
		||||
		multiple sdk.Dec
 | 
			
		||||
		expected sdk.Coins
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "decimals are rounded to nearest even, up",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
			),
 | 
			
		||||
			multiple: sdk.MustNewDecFromStr("3.1415"),
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 3142),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "decimals are rounded to nearest even, down",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e7),
 | 
			
		||||
			),
 | 
			
		||||
			multiple: sdk.MustNewDecFromStr("3.14159265"),
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 31415926),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiple coin amounts are multiplied",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 1e18),
 | 
			
		||||
			),
 | 
			
		||||
			multiple: sdk.MustNewDecFromStr("2.000000000000000002"),
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 2),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2_000_000_000_000_000_002),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "empty coins return nil",
 | 
			
		||||
			coins:    sdk.Coins{},
 | 
			
		||||
			multiple: sdk.MustNewDecFromStr("2.5"),
 | 
			
		||||
			expected: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "nil coins return nil",
 | 
			
		||||
			coins:    nil,
 | 
			
		||||
			multiple: sdk.MustNewDecFromStr("2.5"),
 | 
			
		||||
			expected: nil,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			require.Equal(t,
 | 
			
		||||
				tc.expected,
 | 
			
		||||
				types.MultiplyCoins(tc.coins, tc.multiple),
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFilterCoins(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		coins    sdk.Coins
 | 
			
		||||
		denoms   []string
 | 
			
		||||
		expected sdk.Coins
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "non-empty filter selects subset of coins",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2e3),
 | 
			
		||||
				sdk.NewInt64Coin("btc", 3e3),
 | 
			
		||||
			),
 | 
			
		||||
			denoms: []string{"hard", "btc"},
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("btc", 3e3),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "when coins are nil a non-empty filter returns nil coins",
 | 
			
		||||
			coins:    nil,
 | 
			
		||||
			denoms:   []string{"hard", "btc"},
 | 
			
		||||
			expected: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nil filter returns original coins",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2e3),
 | 
			
		||||
			),
 | 
			
		||||
			denoms: nil,
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2e3),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty filter returns original coins",
 | 
			
		||||
			coins: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2e3),
 | 
			
		||||
			),
 | 
			
		||||
			denoms: []string{},
 | 
			
		||||
			expected: sdk.NewCoins(
 | 
			
		||||
				sdk.NewInt64Coin("hard", 1e3),
 | 
			
		||||
				sdk.NewInt64Coin("ukava", 2e3),
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			require.Equal(t,
 | 
			
		||||
				tc.expected,
 | 
			
		||||
				types.FilterCoins(tc.coins, tc.denoms),
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user