mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 00:37:28 +00:00 
			
		
		
		
	* split up payout.go file * extract genesis builders to new testutil package * move claim integration tests out of keeper * convert claim integration tests to handler tests * combine claim usdx minting keeper methods * combine hard claim keeper methods * combine delegator claim keeper methods * add multiply coins helper method * rename file to better match contents * add basic claiming unit tests * add claiming subset of delegator reward denoms * refactor msg tests * add msg ValidateBasic tests * connect swap hooks into keeper methods * tidy up delegator handler tests * add swap claiming msgs and keeper method * add swap claiming to client * add subset claiming to other msg types * split up handler test file * connect up subset claiming for swap * make multiplier name validation more strict * fix: struct tag typo in swap incentives * connect up subset claiming for hard * connect up subset claiming for delegator * fix: register cli tx routes for swp claiming * fix claim amount in claim event * fix token name in cli help docs * remove unused field in msg tests * tidy up swap and delegator handler tests * refactor hard handler tests * refactor usdx handler tests * remove unused constant Co-authored-by: karzak <kjydavis3@gmail.com>
This commit is contained in:
		
							parent
							
								
									20437a91fb
								
							
						
					
					
						commit
						38a98ac4fc
					
				@ -26,6 +26,7 @@ const (
 | 
			
		||||
	EventTypeRewardPeriod          = types.EventTypeRewardPeriod
 | 
			
		||||
	HardLiquidityProviderClaimType = types.HardLiquidityProviderClaimType
 | 
			
		||||
	Large                          = types.Large
 | 
			
		||||
	MaxDenomsToClaim               = types.MaxDenomsToClaim
 | 
			
		||||
	Medium                         = types.Medium
 | 
			
		||||
	ModuleName                     = types.ModuleName
 | 
			
		||||
	QuerierRoute                   = types.QuerierRoute
 | 
			
		||||
@ -53,7 +54,9 @@ var (
 | 
			
		||||
	NewQuerier                           = keeper.NewQuerier
 | 
			
		||||
	DefaultGenesisState                  = types.DefaultGenesisState
 | 
			
		||||
	DefaultParams                        = types.DefaultParams
 | 
			
		||||
	FilterCoins                          = types.FilterCoins
 | 
			
		||||
	GetTotalVestingPeriodLength          = types.GetTotalVestingPeriodLength
 | 
			
		||||
	MultiplyCoins                        = types.MultiplyCoins
 | 
			
		||||
	NewAccumulator                       = types.NewAccumulator
 | 
			
		||||
	NewDelegatorClaim                    = types.NewDelegatorClaim
 | 
			
		||||
	NewGenesisAccumulationTime           = types.NewGenesisAccumulationTime
 | 
			
		||||
@ -63,6 +66,8 @@ var (
 | 
			
		||||
	NewMsgClaimDelegatorRewardVVesting   = types.NewMsgClaimDelegatorRewardVVesting
 | 
			
		||||
	NewMsgClaimHardReward                = types.NewMsgClaimHardReward
 | 
			
		||||
	NewMsgClaimHardRewardVVesting        = types.NewMsgClaimHardRewardVVesting
 | 
			
		||||
	NewMsgClaimSwapReward                = types.NewMsgClaimSwapReward
 | 
			
		||||
	NewMsgClaimSwapRewardVVesting        = types.NewMsgClaimSwapRewardVVesting
 | 
			
		||||
	NewMsgClaimUSDXMintingReward         = types.NewMsgClaimUSDXMintingReward
 | 
			
		||||
	NewMsgClaimUSDXMintingRewardVVesting = types.NewMsgClaimUSDXMintingRewardVVesting
 | 
			
		||||
	NewMultiRewardIndex                  = types.NewMultiRewardIndex
 | 
			
		||||
@ -98,6 +103,7 @@ var (
 | 
			
		||||
	ErrDecreasingRewardFactor                     = types.ErrDecreasingRewardFactor
 | 
			
		||||
	ErrInsufficientModAccountBalance              = types.ErrInsufficientModAccountBalance
 | 
			
		||||
	ErrInvalidAccountType                         = types.ErrInvalidAccountType
 | 
			
		||||
	ErrInvalidClaimDenoms                         = types.ErrInvalidClaimDenoms
 | 
			
		||||
	ErrInvalidClaimOwner                          = types.ErrInvalidClaimOwner
 | 
			
		||||
	ErrInvalidClaimType                           = types.ErrInvalidClaimType
 | 
			
		||||
	ErrInvalidMultiplier                          = types.ErrInvalidMultiplier
 | 
			
		||||
@ -107,7 +113,6 @@ var (
 | 
			
		||||
	GovDenom                                      = types.GovDenom
 | 
			
		||||
	HardBorrowRewardIndexesKeyPrefix              = types.HardBorrowRewardIndexesKeyPrefix
 | 
			
		||||
	HardLiquidityClaimKeyPrefix                   = types.HardLiquidityClaimKeyPrefix
 | 
			
		||||
	HardLiquidityRewardDenom                      = types.HardLiquidityRewardDenom
 | 
			
		||||
	HardSupplyRewardIndexesKeyPrefix              = types.HardSupplyRewardIndexesKeyPrefix
 | 
			
		||||
	IncentiveMacc                                 = types.IncentiveMacc
 | 
			
		||||
	KeyClaimEnd                                   = types.KeyClaimEnd
 | 
			
		||||
@ -155,6 +160,8 @@ type (
 | 
			
		||||
	MsgClaimDelegatorRewardVVesting   = types.MsgClaimDelegatorRewardVVesting
 | 
			
		||||
	MsgClaimHardReward                = types.MsgClaimHardReward
 | 
			
		||||
	MsgClaimHardRewardVVesting        = types.MsgClaimHardRewardVVesting
 | 
			
		||||
	MsgClaimSwapReward                = types.MsgClaimSwapReward
 | 
			
		||||
	MsgClaimSwapRewardVVesting        = types.MsgClaimSwapRewardVVesting
 | 
			
		||||
	MsgClaimUSDXMintingReward         = types.MsgClaimUSDXMintingReward
 | 
			
		||||
	MsgClaimUSDXMintingRewardVVesting = types.MsgClaimUSDXMintingRewardVVesting
 | 
			
		||||
	MultiRewardIndex                  = types.MultiRewardIndex
 | 
			
		||||
 | 
			
		||||
@ -32,23 +32,21 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
		getCmdClaimHardVVesting(cdc),
 | 
			
		||||
		getCmdClaimDelegator(cdc),
 | 
			
		||||
		getCmdClaimDelegatorVVesting(cdc),
 | 
			
		||||
		getCmdClaimSwap(cdc),
 | 
			
		||||
		getCmdClaimSwapVVesting(cdc),
 | 
			
		||||
	)...)
 | 
			
		||||
 | 
			
		||||
	return incentiveTxCmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimCdp(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim-cdp [multiplier]",
 | 
			
		||||
		Short: "claim CDP rewards using a given multiplier",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding CDP rewards using a given multiplier
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-cdp large
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		Args: cobra.ExactArgs(1),
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:     "claim-cdp [multiplier]",
 | 
			
		||||
		Short:   "claim USDX minting rewards using a given multiplier",
 | 
			
		||||
		Long:    `Claim sender's outstanding USDX minting rewards using a given multiplier.`,
 | 
			
		||||
		Example: fmt.Sprintf(`  $ %s tx %s claim-cdp large`, version.ClientName, types.ModuleName),
 | 
			
		||||
		Args:    cobra.ExactArgs(1),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
@ -58,27 +56,25 @@ func getCmdClaimCdp(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimUSDXMintingReward(sender, multiplier)
 | 
			
		||||
			err := msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimCdpVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim-cdp-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim CDP rewards using a given multiplier on behalf of a validator vesting account",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding CDP rewards on behalf of a validator vesting using a given multiplier
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-cdp-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "claim-cdp-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim USDX minting rewards using a given multiplier on behalf of a validator vesting account",
 | 
			
		||||
		Long: `Claim sender's outstanding USDX minting rewards on behalf of a validator vesting using a given multiplier.
 | 
			
		||||
A receiver address for the rewards is needed as validator vesting accounts cannot receive locked tokens.`,
 | 
			
		||||
		Example: fmt.Sprintf(`  $ %s tx %s claim-cdp-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw`, version.ClientName, types.ModuleName),
 | 
			
		||||
		Args:    cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
@ -93,62 +89,25 @@ func getCmdClaimCdpVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimUSDXMintingRewardVVesting(sender, receiver, multiplier)
 | 
			
		||||
			err = msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimHardVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &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: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding Hard rewards on behalf of a validator vesting account for deposit/borrow/delegate using given multiplier
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-hard-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
 | 
			
		||||
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
			receiverStr := args[1]
 | 
			
		||||
			receiver, err := sdk.AccAddressFromBech32(receiverStr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimHardRewardVVesting(sender, receiver, multiplier)
 | 
			
		||||
			err = msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimHard(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim-hard [multiplier]",
 | 
			
		||||
		Short: "claim sender's Hard module rewards using a given multiplier",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding Hard rewards for deposit/borrow/delegate using given multiplier
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-hard large
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		Args: cobra.ExactArgs(1),
 | 
			
		||||
	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),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
@ -157,57 +116,31 @@ func getCmdClaimHard(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
			sender := cliCtx.GetFromAddress()
 | 
			
		||||
			multiplier := args[0]
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimHardReward(sender, multiplier)
 | 
			
		||||
			err := msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			msg := types.NewMsgClaimHardReward(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 getCmdClaimDelegator(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim-delegator [multiplier]",
 | 
			
		||||
		Short: "claim sender's delegator rewards using a given multiplier",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding delegator rewards using given multiplier
 | 
			
		||||
func getCmdClaimHardVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []string
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-delegator large
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
		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]
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorReward(sender, multiplier)
 | 
			
		||||
			err := msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCmdClaimDelegatorVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "claim-delegator-vesting [multiplier] [receiver]",
 | 
			
		||||
		Short: "claim delegator rewards on behalf of a validator vesting account using a given multiplier",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Claim sender's outstanding delegator rewards on behalf of a validator vesting account using given multiplier
 | 
			
		||||
 | 
			
		||||
			Example:
 | 
			
		||||
			$ %s tx %s claim-delegator-vesting large kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw
 | 
			
		||||
		`, version.ClientName, types.ModuleName),
 | 
			
		||||
		),
 | 
			
		||||
	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.`,
 | 
			
		||||
		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),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		Args: cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
@ -222,12 +155,158 @@ func getCmdClaimDelegatorVVesting(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorRewardVVesting(sender, receiver, multiplier)
 | 
			
		||||
			err = msg.ValidateBasic()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			msg := types.NewMsgClaimHardRewardVVesting(sender, receiver, 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 getCmdClaimDelegator(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []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.`,
 | 
			
		||||
		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),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		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]
 | 
			
		||||
 | 
			
		||||
			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)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimDelegatorRewardVVesting(sender, receiver, 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 getCmdClaimSwap(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	var denomsToClaim []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.`,
 | 
			
		||||
		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),
 | 
			
		||||
		}, "\n"),
 | 
			
		||||
		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]
 | 
			
		||||
 | 
			
		||||
			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)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimSwapRewardVVesting(sender, receiver, 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostClaimReq defines the properties of claim transaction's request body.
 | 
			
		||||
@ -33,4 +34,5 @@ type PostClaimVVestingReq 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,15 +16,33 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	r.HandleFunc("/incentive/claim-cdp", postClaimCdpHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-cdp-vesting", postClaimCdpVVestingHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-hard", postClaimHardHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-hard-vesting", postClaimHardVVestingHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-delegator", postClaimDelegatorHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-delegator-vesting", postClaimDelegatorVVestingHandlerFn(cliCtx)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-cdp", postClaimHandlerFn(cliCtx, usdxMintingGenerator)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-cdp-vesting", postClaimVVestingHandlerFn(cliCtx, usdxMintingVVGenerator)).Methods("POST")
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/incentive/claim-hard", postClaimHandlerFn(cliCtx, hardGenerator)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-hard-vesting", postClaimVVestingHandlerFn(cliCtx, hardVVGenerator)).Methods("POST")
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/incentive/claim-delegator", postClaimHandlerFn(cliCtx, delegatorGenerator)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-delegator-vesting", postClaimVVestingHandlerFn(cliCtx, delegatorVVGenerator)).Methods("POST")
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/incentive/claim-swap", postClaimHandlerFn(cliCtx, swapGenerator)).Methods("POST")
 | 
			
		||||
	r.HandleFunc("/incentive/claim-swap-vesting", postClaimVVestingHandlerFn(cliCtx, swapVVGenerator)).Methods("POST")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
func usdxMintingGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingReward(req.Sender, req.MultiplierName)
 | 
			
		||||
}
 | 
			
		||||
func hardGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimHardReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
func delegatorGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimDelegatorReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
func swapGenerator(req PostClaimReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimSwapReward(req.Sender, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimReq) sdk.Msg) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
@ -47,7 +65,7 @@ func postClaimCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimUSDXMintingReward(requestBody.Sender, requestBody.MultiplierName)
 | 
			
		||||
		msg := msgGenerator(requestBody)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
@ -57,7 +75,20 @@ func postClaimCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimCdpVVestingHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
func usdxMintingVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimUSDXMintingRewardVVesting(req.Sender, req.Receiver, req.MultiplierName)
 | 
			
		||||
}
 | 
			
		||||
func hardVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimHardRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
func delegatorVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimDelegatorRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
func swapVVGenerator(req PostClaimVVestingReq) sdk.Msg {
 | 
			
		||||
	return types.NewMsgClaimSwapRewardVVesting(req.Sender, req.Receiver, req.MultiplierName, req.DenomsToClaim)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimVVestingHandlerFn(cliCtx context.CLIContext, msgGenerator func(req PostClaimVVestingReq) sdk.Msg) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimVVestingReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
@ -80,139 +111,7 @@ func postClaimCdpVVestingHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimUSDXMintingRewardVVesting(requestBody.Sender, requestBody.Receiver, requestBody.MultiplierName)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimHardHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		requestBody.BaseReq = requestBody.BaseReq.Sanitize()
 | 
			
		||||
		if !requestBody.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fromAddr, err := sdk.AccAddressFromBech32(requestBody.BaseReq.From)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !bytes.Equal(fromAddr, requestBody.Sender) {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("expected: %s, got: %s", fromAddr, requestBody.Sender))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimHardReward(requestBody.Sender, requestBody.MultiplierName)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimHardVVestingHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimVVestingReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		requestBody.BaseReq = requestBody.BaseReq.Sanitize()
 | 
			
		||||
		if !requestBody.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fromAddr, err := sdk.AccAddressFromBech32(requestBody.BaseReq.From)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !bytes.Equal(fromAddr, requestBody.Sender) {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("expected: %s, got: %s", fromAddr, requestBody.Sender))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimHardRewardVVesting(requestBody.Sender, requestBody.Receiver, requestBody.MultiplierName)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimDelegatorHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		requestBody.BaseReq = requestBody.BaseReq.Sanitize()
 | 
			
		||||
		if !requestBody.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fromAddr, err := sdk.AccAddressFromBech32(requestBody.BaseReq.From)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !bytes.Equal(fromAddr, requestBody.Sender) {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("expected: %s, got: %s", fromAddr, requestBody.Sender))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimDelegatorReward(requestBody.Sender, requestBody.MultiplierName)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postClaimDelegatorVVestingHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var requestBody PostClaimVVestingReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		requestBody.BaseReq = requestBody.BaseReq.Sanitize()
 | 
			
		||||
		if !requestBody.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fromAddr, err := sdk.AccAddressFromBech32(requestBody.BaseReq.From)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !bytes.Equal(fromAddr, requestBody.Sender) {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("expected: %s, got: %s", fromAddr, requestBody.Sender))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msg := types.NewMsgClaimDelegatorRewardVVesting(requestBody.Sender, requestBody.Receiver, requestBody.MultiplierName)
 | 
			
		||||
		msg := msgGenerator(requestBody)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
@ -85,7 +85,7 @@ func (suite *GenesisTestSuite) SetupTest() {
 | 
			
		||||
		app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(incentiveGS)},
 | 
			
		||||
		app.GenesisState{hard.ModuleName: hard.ModuleCdc.MustMarshalJSON(hardGS)},
 | 
			
		||||
		NewCDPGenStateMulti(),
 | 
			
		||||
		NewPricefeedGenStateMulti(),
 | 
			
		||||
		NewPricefeedGenStateMultiFromTime(suite.genesisTime),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
@ -115,7 +115,7 @@ func (suite *GenesisTestSuite) TestPaidOutClaimsPassValidateGenesis() {
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	incentiveHandler := incentive.NewHandler(suite.keeper)
 | 
			
		||||
	_, err = incentiveHandler(suite.ctx, incentive.NewMsgClaimHardReward(suite.addrs[0], string(incentive.Large)))
 | 
			
		||||
	_, err = incentiveHandler(suite.ctx, incentive.NewMsgClaimHardReward(suite.addrs[0], string(incentive.Large), nil))
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	genState := incentive.ExportGenesis(suite.ctx, suite.keeper)
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,10 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
 | 
			
		||||
			return handleMsgClaimDelegatorReward(ctx, k, msg)
 | 
			
		||||
		case types.MsgClaimDelegatorRewardVVesting:
 | 
			
		||||
			return handleMsgClaimDelegatorRewardVVesting(ctx, k, msg)
 | 
			
		||||
		case types.MsgClaimSwapReward:
 | 
			
		||||
			return handleMsgClaimSwapReward(ctx, k, msg)
 | 
			
		||||
		case types.MsgClaimSwapRewardVVesting:
 | 
			
		||||
			return handleMsgClaimSwapRewardVVesting(ctx, k, msg)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
 | 
			
		||||
		}
 | 
			
		||||
@ -32,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, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -43,7 +47,10 @@ func handleMsgClaimUSDXMintingReward(ctx sdk.Context, k keeper.Keeper, msg types
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimUSDXMintingRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimUSDXMintingRewardVVesting) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimUSDXMintingRewardVVesting(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	if err := k.ValidateIsValidatorVestingAccount(ctx, msg.Sender); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err := k.ClaimUSDXMintingReward(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -54,7 +61,7 @@ func handleMsgClaimUSDXMintingRewardVVesting(ctx sdk.Context, k keeper.Keeper, m
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimHardReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimHardReward) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimHardReward(ctx, msg.Sender, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	err := k.ClaimHardReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -65,7 +72,10 @@ func handleMsgClaimHardReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgCla
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimHardRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimHardRewardVVesting) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimHardRewardVVesting(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
@ -76,7 +86,7 @@ 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, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	err := k.ClaimDelegatorReward(ctx, msg.Sender, msg.Sender, types.MultiplierName(msg.MultiplierName), msg.DenomsToClaim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -87,7 +97,35 @@ func handleMsgClaimDelegatorReward(ctx sdk.Context, k keeper.Keeper, msg types.M
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimDelegatorRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimDelegatorRewardVVesting) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	err := k.ClaimDelegatorRewardVVesting(ctx, msg.Sender, msg.Receiver, types.MultiplierName(msg.MultiplierName))
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	}
 | 
			
		||||
	return &sdk.Result{
 | 
			
		||||
		Events: ctx.EventManager().Events(),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleMsgClaimSwapRewardVVesting(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimSwapRewardVVesting) (*sdk.Result, error) {
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								x/incentive/handler_delegator_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								x/incentive/handler_delegator_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
package incentive_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaim() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
	receiverAddr := suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("ukava", 1e12))).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a delegation (need to create a validator first, which will have a self delegation)
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverMsgCreateValidator(sdk.ValAddress(userAddr), c("ukava", 1e9)),
 | 
			
		||||
	)
 | 
			
		||||
	// new block required to bond validator
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
	// Now the delegation is bonded, accumulate some delegator rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, receiverAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim a single denom
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 2*7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.DelegatorRewardEquals(userAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaimSingleDenom() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("ukava", 1e12)))
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a delegation (need to create a validator first, which will have a self delegation)
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverMsgCreateValidator(sdk.ValAddress(userAddr), c("ukava", 1e9)),
 | 
			
		||||
	)
 | 
			
		||||
	// new block required to bond validator
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
	// Now the delegation is bonded, accumulate some delegator rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, 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() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a delegation (need to create a validator first, which will have a self delegation)
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverMsgCreateValidator(sdk.ValAddress(valAddr), c("ukava", 1e9)),
 | 
			
		||||
	)
 | 
			
		||||
	// new block required to bond validator
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
	// Now the delegation is bonded, accumulate some delegator rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("hard", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
	suite.DelegatorRewardEquals(valAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutDelegatorClaimVVestingSingleDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(types.BondDenom, cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a delegation (need to create a validator first, which will have a self delegation)
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverMsgCreateValidator(sdk.ValAddress(valAddr), c("ukava", 1e9)),
 | 
			
		||||
	)
 | 
			
		||||
	// new block required to bond validator
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
	// Now the delegation is bonded, accumulate some delegator rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimDelegatorRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004786, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.DelegatorRewardEquals(valAddr, cs(c("hard", 2*7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										195
									
								
								x/incentive/handler_hard_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								x/incentive/handler_hard_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,195 @@
 | 
			
		||||
package incentive_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaim() {
 | 
			
		||||
	userAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("bnb", 1e12))).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a deposit and borrow
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgDeposit(userAddr, cs(c("bnb", 1e11))))
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgBorrow(userAddr, cs(c("bnb", 1e10))))
 | 
			
		||||
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, receiverAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim a single denom
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 2*7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.HardRewardEquals(userAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaimSingleDenom() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("bnb", 1e12)))
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a deposit and borrow
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgDeposit(userAddr, cs(c("bnb", 1e11))))
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgBorrow(userAddr, cs(c("bnb", 1e10))))
 | 
			
		||||
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(userAddr, suite.addrs[1], "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, 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() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("bnb", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a deposit and borrow
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgDeposit(valAddr, cs(c("bnb", 1e11))))
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgBorrow(valAddr, cs(c("bnb", 1e10))))
 | 
			
		||||
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("hard", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
	suite.HardRewardEquals(valAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutHardClaimVVestingSingleDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("bnb", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSupplyRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6))).
 | 
			
		||||
		WithSimpleBorrowRewardPeriod("bnb", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// create a deposit and borrow
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgDeposit(valAddr, cs(c("bnb", 1e11))))
 | 
			
		||||
	suite.NoError(suite.DeliverHardMsgBorrow(valAddr, cs(c("bnb", 1e10))))
 | 
			
		||||
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimHardRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 2*7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.HardRewardEquals(valAddr, cs(c("hard", 2*7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										298
									
								
								x/incentive/handler_swap_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								x/incentive/handler_swap_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,298 @@
 | 
			
		||||
package incentive_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
	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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHandlerTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(HandlerTestSuite))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetupTest is run automatically before each suite test
 | 
			
		||||
func (suite *HandlerTestSuite) SetupTest() {
 | 
			
		||||
	config := sdk.GetConfig()
 | 
			
		||||
	app.SetBech32AddressPrefixes(config)
 | 
			
		||||
 | 
			
		||||
	_, suite.addrs = app.GeneratePrivKeyAddressPairs(5)
 | 
			
		||||
 | 
			
		||||
	suite.genesisTime = time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type genesisBuilder interface {
 | 
			
		||||
	BuildMarshalled() app.GenesisState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) SetupWithGenState(builders ...genesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	builtGenStates := []app.GenesisState{
 | 
			
		||||
		NewStakingGenesisState(),
 | 
			
		||||
		NewPricefeedGenStateMultiFromTime(suite.genesisTime),
 | 
			
		||||
		NewCDPGenStateMulti(),
 | 
			
		||||
		NewHardGenStateMulti(suite.genesisTime).BuildMarshalled(),
 | 
			
		||||
		NewSwapGenesisState(),
 | 
			
		||||
	}
 | 
			
		||||
	for _, builder := range builders {
 | 
			
		||||
		builtGenStates = append(builtGenStates, builder.BuildMarshalled())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	suite.App.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
		suite.genesisTime,
 | 
			
		||||
		builtGenStates...,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for the purposes of incentive module. A validator vesting account only needs to exist, and have enough balance to delegate/or supply.
 | 
			
		||||
func (suite *HandlerTestSuite) NewValidatorVestingAccountWithBalance(address sdk.AccAddress, spendableBalance sdk.Coins) *validatorvesting.ValidatorVestingAccount {
 | 
			
		||||
	bacc := auth.NewBaseAccount(address, spendableBalance, nil, 0, 0)
 | 
			
		||||
	// vesting coins set to nil and vesting end time set to genesis full base account balance should be spendable
 | 
			
		||||
	bva, err := vesting.NewBaseVestingAccount(bacc, nil, suite.genesisTime.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	// vesting start time set to genesis and no vesting periods
 | 
			
		||||
	return validatorvesting.NewValidatorVestingAccountRaw(bva, suite.genesisTime.Unix(), nil, sdk.ConsAddress{}, nil, 90)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// authBuilder returns a new auth genesis builder with a full kavadist module account.
 | 
			
		||||
func (suite *HandlerTestSuite) authBuilder() app.AuthGenesisBuilder {
 | 
			
		||||
	return app.NewAuthGenesisBuilder().
 | 
			
		||||
		WithSimpleModuleAccount(kavadist.ModuleName, cs(c(types.USDXMintingRewardDenom, 1e18), c("hard", 1e18), c("swap", 1e18)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// incentiveBuilder returns a new incentive genesis builder with a genesis time and multipliers set
 | 
			
		||||
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")),
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaim() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("ukava", 1e12), c("busd", 1e12)))
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSwapRewardPeriod("busd/ukava", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// deposit into a swap pool
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverSwapMsgDeposit(userAddr, c("ukava", 1e9), c("busd", 1e9), d("1.0")),
 | 
			
		||||
	)
 | 
			
		||||
	// accumulate some swap rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("swap", 7*1e6), c("hard", 7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
	suite.SwapRewardEquals(userAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaimSingleDenom() {
 | 
			
		||||
	userAddr := suite.addrs[0]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("ukava", 1e12), c("busd", 1e12)))
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSwapRewardPeriod("busd/ukava", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// deposit into a swap pool
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverSwapMsgDeposit(userAddr, c("ukava", 1e9), c("busd", 1e9), d("1.0")),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// accumulate some swap rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(userAddr, suite.addrs[1], "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(userAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 7*1e6)
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, 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() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12), c("busd", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSwapRewardPeriod("busd/ukava", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// deposit into a swap pool
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverSwapMsgDeposit(valAddr, c("ukava", 1e9), c("busd", 1e9), d("1.0")),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// accumulate some swap rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, "large", nil),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c("hard", 7*1e6), c("swap", 7*1e6))
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
	suite.SwapRewardEquals(valAddr, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutSwapClaimVVestingSingleDenom() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("ukava", 1e12), c("busd", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleSwapRewardPeriod("busd/ukava", cs(c("hard", 1e6), c("swap", 1e6)))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// deposit into a swap pool
 | 
			
		||||
	suite.NoError(
 | 
			
		||||
		suite.DeliverSwapMsgDeposit(valAddr, c("ukava", 1e9), c("busd", 1e9), d("1.0")),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// accumulate some swap rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapReward(valAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimSwapRewardVVesting(valAddr, receiverAddr, "large", []string{"swap"}),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c("swap", 7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.SwapRewardEquals(valAddr, cs(c("hard", 7*1e6)))
 | 
			
		||||
}
 | 
			
		||||
@ -1,121 +0,0 @@
 | 
			
		||||
package incentive_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func cs(coins ...sdk.Coin) sdk.Coins        { return sdk.NewCoins(coins...) }
 | 
			
		||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
 | 
			
		||||
 | 
			
		||||
type HandlerTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	ctx     sdk.Context
 | 
			
		||||
	app     app.TestApp
 | 
			
		||||
	handler sdk.Handler
 | 
			
		||||
	keeper  incentive.Keeper
 | 
			
		||||
	addrs   []sdk.AccAddress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) SetupTest() {
 | 
			
		||||
	tApp := app.NewTestApp()
 | 
			
		||||
	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
 | 
			
		||||
	keeper := tApp.GetIncentiveKeeper()
 | 
			
		||||
 | 
			
		||||
	// Set up genesis state and initialize
 | 
			
		||||
	_, addrs := app.GeneratePrivKeyAddressPairs(3)
 | 
			
		||||
	coins := []sdk.Coins{}
 | 
			
		||||
	for j := 0; j < 3; j++ {
 | 
			
		||||
		coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
 | 
			
		||||
	}
 | 
			
		||||
	authGS := app.NewAuthGenState(addrs, coins)
 | 
			
		||||
	incentiveGS := incentive.NewGenesisState(
 | 
			
		||||
		incentive.NewParams(
 | 
			
		||||
			incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "bnb", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "ukava", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
 | 
			
		||||
			incentive.MultiRewardPeriods{incentive.NewMultiRewardPeriod(true, "btcb/usdx", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), cs(c("ukava", 122354)))},
 | 
			
		||||
			incentive.Multipliers{incentive.NewMultiplier(incentive.MultiplierName("small"), 1, d("0.25")), incentive.NewMultiplier(incentive.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
			time.Date(2025, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
			
		||||
		),
 | 
			
		||||
		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
		incentive.DefaultUSDXClaims,
 | 
			
		||||
		incentive.DefaultHardClaims,
 | 
			
		||||
		incentive.DefaultDelegatorClaims,
 | 
			
		||||
		incentive.DefaultSwapClaims,
 | 
			
		||||
	)
 | 
			
		||||
	tApp.InitializeFromGenesisStates(authGS, app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(incentiveGS)}, NewCDPGenStateMulti(), NewPricefeedGenStateMulti())
 | 
			
		||||
 | 
			
		||||
	suite.addrs = addrs
 | 
			
		||||
	suite.handler = incentive.NewHandler(keeper)
 | 
			
		||||
	suite.keeper = keeper
 | 
			
		||||
	suite.app = tApp
 | 
			
		||||
	suite.ctx = ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestMsgUSDXMintingClaimReward() {
 | 
			
		||||
	suite.addUSDXMintingClaim()
 | 
			
		||||
	msg := incentive.NewMsgClaimUSDXMintingReward(suite.addrs[0], "small")
 | 
			
		||||
	res, err := suite.handler(suite.ctx, msg)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.Require().NotNil(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestMsgHardClaimReward() {
 | 
			
		||||
	suite.addHardLiquidityProviderClaim()
 | 
			
		||||
	msg := incentive.NewMsgClaimHardReward(suite.addrs[0], "small")
 | 
			
		||||
	res, err := suite.handler(suite.ctx, msg)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	suite.Require().NotNil(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) addHardLiquidityProviderClaim() {
 | 
			
		||||
	sk := suite.app.GetSupplyKeeper()
 | 
			
		||||
	err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000)))
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	rewardPeriod := types.RewardIndexes{types.NewRewardIndex("bnb-s", sdk.ZeroDec())}
 | 
			
		||||
	multiRewardIndex := types.NewMultiRewardIndex("bnb-s", rewardPeriod)
 | 
			
		||||
	multiRewardIndexes := types.MultiRewardIndexes{multiRewardIndex}
 | 
			
		||||
	c1 := incentive.NewHardLiquidityProviderClaim(suite.addrs[0], cs(c("ukava", 1000000)), multiRewardIndexes, multiRewardIndexes)
 | 
			
		||||
	suite.NotPanics(func() {
 | 
			
		||||
		suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, c1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) addUSDXMintingClaim() {
 | 
			
		||||
	sk := suite.app.GetSupplyKeeper()
 | 
			
		||||
	err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000)))
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	c1 := incentive.NewUSDXMintingClaim(suite.addrs[0], c("ukava", 1000000), types.RewardIndexes{types.NewRewardIndex("bnb-s", sdk.ZeroDec())})
 | 
			
		||||
	suite.NotPanics(func() {
 | 
			
		||||
		suite.keeper.SetUSDXMintingClaim(suite.ctx, c1)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: add tests
 | 
			
		||||
 | 
			
		||||
func TestHandlerTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(HandlerTestSuite))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Avoid cluttering test cases with long function names
 | 
			
		||||
func i(in int64) sdk.Int   { return sdk.NewInt(in) }
 | 
			
		||||
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
 | 
			
		||||
							
								
								
									
										96
									
								
								x/incentive/handler_usdx_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								x/incentive/handler_usdx_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
package incentive_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutUSDXClaim() {
 | 
			
		||||
	userAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithSimpleAccount(userAddr, cs(c("bnb", 1e12))).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// mint some usdx
 | 
			
		||||
	suite.DeliverMsgCreateCDP(userAddr, c("bnb", 1e9), c("usdx", 1e7), "bnb-a")
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(userAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by vvesting claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimUSDXMintingRewardVVesting(userAddr, receiverAddr, "large"),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim a single denom
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimUSDXMintingReward(userAddr, "large"),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := cs(c(types.USDXMintingRewardDenom, 7*1e6))
 | 
			
		||||
	suite.BalanceEquals(userAddr, preClaimBal.Add(expectedRewards...))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(userAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: expectedRewards},
 | 
			
		||||
	})
 | 
			
		||||
	// Check that claimed coins have been removed from a claim's reward
 | 
			
		||||
	suite.USDXRewardEquals(userAddr, c(types.USDXMintingRewardDenom, 0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *HandlerTestSuite) TestPayoutUSDXClaimVVesting() {
 | 
			
		||||
	valAddr, receiverAddr := suite.addrs[0], suite.addrs[1]
 | 
			
		||||
 | 
			
		||||
	vva := suite.NewValidatorVestingAccountWithBalance(valAddr, cs(c("bnb", 1e12)))
 | 
			
		||||
 | 
			
		||||
	authBulder := suite.authBuilder().
 | 
			
		||||
		WithAccounts(vva).
 | 
			
		||||
		WithSimpleAccount(receiverAddr, nil)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := suite.incentiveBuilder().
 | 
			
		||||
		WithSimpleUSDXRewardPeriod("bnb-a", c(types.USDXMintingRewardDenom, 1e6))
 | 
			
		||||
 | 
			
		||||
	suite.SetupWithGenState(authBulder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
	// mint some usdx
 | 
			
		||||
	suite.DeliverMsgCreateCDP(valAddr, c("bnb", 1e9), c("usdx", 1e7), "bnb-a")
 | 
			
		||||
	// accumulate some rewards
 | 
			
		||||
	suite.NextBlockAfter(7 * time.Second)
 | 
			
		||||
 | 
			
		||||
	preClaimBal := suite.GetBalance(receiverAddr)
 | 
			
		||||
 | 
			
		||||
	// Check rewards cannot be claimed by normal claim msgs
 | 
			
		||||
	err := suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimUSDXMintingReward(valAddr, "large"),
 | 
			
		||||
	)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidAccountType)
 | 
			
		||||
 | 
			
		||||
	// Claim rewards
 | 
			
		||||
	err = suite.DeliverIncentiveMsg(
 | 
			
		||||
		types.NewMsgClaimUSDXMintingRewardVVesting(valAddr, receiverAddr, "large"),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Check rewards were paid out
 | 
			
		||||
	expectedRewards := c(types.USDXMintingRewardDenom, 7*1e6)
 | 
			
		||||
	suite.BalanceEquals(receiverAddr, preClaimBal.Add(expectedRewards))
 | 
			
		||||
 | 
			
		||||
	suite.VestingPeriodsEqual(receiverAddr, vesting.Periods{
 | 
			
		||||
		{Length: 33004793, Amount: cs(expectedRewards)},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
	suite.USDXRewardEquals(valAddr, c(types.USDXMintingRewardDenom, 0))
 | 
			
		||||
}
 | 
			
		||||
@ -4,12 +4,21 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/staking"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/cdp"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/pricefeed"
 | 
			
		||||
	"github.com/kava-labs/kava/x/swap"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Avoid cluttering test cases with long function names
 | 
			
		||||
func i(in int64) sdk.Int                    { return sdk.NewInt(in) }
 | 
			
		||||
func d(str string) sdk.Dec                  { return sdk.MustNewDecFromStr(str) }
 | 
			
		||||
func cs(coins ...sdk.Coin) sdk.Coins        { return sdk.NewCoins(coins...) }
 | 
			
		||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
 | 
			
		||||
 | 
			
		||||
func NewCDPGenStateMulti() app.GenesisState {
 | 
			
		||||
	cdpGenesis := cdp.GenesisState{
 | 
			
		||||
		Params: cdp.Params{
 | 
			
		||||
@ -99,120 +108,92 @@ func NewCDPGenStateMulti() app.GenesisState {
 | 
			
		||||
	return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewPricefeedGenStateMulti() app.GenesisState {
 | 
			
		||||
func NewPricefeedGenStateMultiFromTime(t time.Time) app.GenesisState {
 | 
			
		||||
	pfGenesis := pricefeed.GenesisState{
 | 
			
		||||
		Params: pricefeed.Params{
 | 
			
		||||
			Markets: []pricefeed.Market{
 | 
			
		||||
				{MarketID: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
				{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
				{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
				{MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
				{MarketID: "busd:usd", BaseAsset: "busd", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
				{MarketID: "zzz:usd", BaseAsset: "zzz", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		PostedPrices: []pricefeed.PostedPrice{
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "kava:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.MustNewDecFromStr("2.00"),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "btc:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.MustNewDecFromStr("8000.00"),
 | 
			
		||||
				Expiry:        time.Now().Add(1 * time.Hour),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "xrp:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.MustNewDecFromStr("0.25"),
 | 
			
		||||
				Expiry:        time.Now().Add(1 * time.Hour),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "bnb:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.MustNewDecFromStr("17.25"),
 | 
			
		||||
				Expiry:        time.Now().Add(1 * time.Hour),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "busd:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.OneDec(),
 | 
			
		||||
				Expiry:        time.Now().Add(1 * time.Hour),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				MarketID:      "zzz:usd",
 | 
			
		||||
				OracleAddress: sdk.AccAddress{},
 | 
			
		||||
				Price:         sdk.MustNewDecFromStr("2.00"),
 | 
			
		||||
				Expiry:        t.Add(1 * time.Hour),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func NewIncentiveGenState(previousAccumTime, endTime time.Time, rewardPeriods ...incentive.RewardPeriod) app.GenesisState {
 | 
			
		||||
// 	var accumulationTimes incentive.GenesisAccumulationTimes
 | 
			
		||||
// 	for _, rp := range rewardPeriods {
 | 
			
		||||
// 		accumulationTimes = append(
 | 
			
		||||
// 			accumulationTimes,
 | 
			
		||||
// 			incentive.NewGenesisAccumulationTime(
 | 
			
		||||
// 				rp.CollateralType,
 | 
			
		||||
// 				previousAccumTime,
 | 
			
		||||
// 			),
 | 
			
		||||
// 		)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	genesis := incentive.NewGenesisState(
 | 
			
		||||
// 		incentive.NewParams(
 | 
			
		||||
// 			rewardPeriods,
 | 
			
		||||
// 			types.MultiRewardPeriods{},
 | 
			
		||||
// 			types.MultiRewardPeriods{},
 | 
			
		||||
// 			types.MultiRewardPeriods{},
 | 
			
		||||
// 			types.MultiRewardPeriods{},
 | 
			
		||||
// 			incentive.Multipliers{
 | 
			
		||||
// 				incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
 | 
			
		||||
// 				incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
 | 
			
		||||
// 			},
 | 
			
		||||
// 			endTime,
 | 
			
		||||
// 		),
 | 
			
		||||
// 		accumulationTimes,
 | 
			
		||||
// 		accumulationTimes,
 | 
			
		||||
// 		accumulationTimes,
 | 
			
		||||
// 		incentive.DefaultGenesisAccumulationTimes,
 | 
			
		||||
// 		incentive.DefaultUSDXClaims,
 | 
			
		||||
// 		incentive.DefaultHardClaims,
 | 
			
		||||
// 	)
 | 
			
		||||
// 	return app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(genesis)}
 | 
			
		||||
// }
 | 
			
		||||
func NewHardGenStateMulti(genTime time.Time) testutil.HardGenesisBuilder {
 | 
			
		||||
	kavaMM := testutil.NewStandardMoneyMarket("ukava")
 | 
			
		||||
	kavaMM.SpotMarketID = "kava:usd"
 | 
			
		||||
	btcMM := testutil.NewStandardMoneyMarket("btcb")
 | 
			
		||||
	btcMM.SpotMarketID = "btc:usd"
 | 
			
		||||
 | 
			
		||||
func NewCDPGenStateHighInterest() app.GenesisState {
 | 
			
		||||
	cdpGenesis := cdp.GenesisState{
 | 
			
		||||
		Params: cdp.Params{
 | 
			
		||||
			GlobalDebtLimit:         sdk.NewInt64Coin("usdx", 2000000000000),
 | 
			
		||||
			SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
 | 
			
		||||
			SurplusAuctionLot:       cdp.DefaultSurplusLot,
 | 
			
		||||
			DebtAuctionThreshold:    cdp.DefaultDebtThreshold,
 | 
			
		||||
			DebtAuctionLot:          cdp.DefaultDebtLot,
 | 
			
		||||
			CollateralParams: cdp.CollateralParams{
 | 
			
		||||
				{
 | 
			
		||||
					Denom:               "bnb",
 | 
			
		||||
					Type:                "bnb-a",
 | 
			
		||||
					LiquidationRatio:    sdk.MustNewDecFromStr("1.5"),
 | 
			
		||||
					DebtLimit:           sdk.NewInt64Coin("usdx", 500000000000),
 | 
			
		||||
					StabilityFee:        sdk.MustNewDecFromStr("1.000000051034942716"), // 500% APR
 | 
			
		||||
					LiquidationPenalty:  d("0.05"),
 | 
			
		||||
					AuctionSize:         i(50000000000),
 | 
			
		||||
					Prefix:              0x22,
 | 
			
		||||
					SpotMarketID:        "bnb:usd",
 | 
			
		||||
					LiquidationMarketID: "bnb:usd",
 | 
			
		||||
					ConversionFactor:    i(8),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			DebtParam: cdp.DebtParam{
 | 
			
		||||
				Denom:            "usdx",
 | 
			
		||||
				ReferenceAsset:   "usd",
 | 
			
		||||
				ConversionFactor: i(6),
 | 
			
		||||
				DebtFloor:        i(10000000),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		StartingCdpID: cdp.DefaultCdpStartingID,
 | 
			
		||||
		DebtDenom:     cdp.DefaultDebtDenom,
 | 
			
		||||
		GovDenom:      cdp.DefaultGovDenom,
 | 
			
		||||
		CDPs:          cdp.CDPs{},
 | 
			
		||||
		PreviousAccumulationTimes: cdp.GenesisAccumulationTimes{
 | 
			
		||||
			cdp.NewGenesisAccumulationTime("bnb-a", time.Time{}, sdk.OneDec()),
 | 
			
		||||
		},
 | 
			
		||||
		TotalPrincipals: cdp.GenesisTotalPrincipals{
 | 
			
		||||
			cdp.NewGenesisTotalPrincipal("bnb-a", sdk.ZeroInt()),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
 | 
			
		||||
	builder := testutil.NewHardGenesisBuilder().WithGenesisTime(genTime).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("usdx")).
 | 
			
		||||
		WithInitializedMoneyMarket(kavaMM).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("bnb")).
 | 
			
		||||
		WithInitializedMoneyMarket(btcMM).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("xrp")).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("zzz"))
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewStakingGenesisState() app.GenesisState {
 | 
			
		||||
	genState := staking.DefaultGenesisState()
 | 
			
		||||
	genState.Params.BondDenom = "ukava"
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		staking.ModuleName: staking.ModuleCdc.MustMarshalJSON(genState),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSwapGenesisState() app.GenesisState {
 | 
			
		||||
	genesis := swap.NewGenesisState(
 | 
			
		||||
		swap.NewParams(
 | 
			
		||||
			swap.NewAllowedPools(swap.NewAllowedPool("busd", "ukava")),
 | 
			
		||||
			d("0.0"),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		swap.ModuleName: swap.ModuleCdc.MustMarshalJSON(genesis),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import (
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -23,7 +24,7 @@ func TestRiskyCDPsAccumulateRewards(t *testing.T) {
 | 
			
		||||
	collateralType := "bnb-a"
 | 
			
		||||
	rewardsPerSecond := c(types.USDXMintingRewardDenom, 1_000_000)
 | 
			
		||||
 | 
			
		||||
	incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
	incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(genesisTime).
 | 
			
		||||
		WithSimpleUSDXRewardPeriod(collateralType, rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										239
									
								
								x/incentive/keeper/claim.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								x/incentive/keeper/claim.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,239 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	claim, found := k.GetUSDXMintingClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claim, err := k.SynchronizeUSDXMintingClaim(ctx, claim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rewardAmount := claim.Reward.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
	if rewardAmount.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount)
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, sdk.NewCoins(rewardCoin), length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroUSDXMintingClaim(ctx, claim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, owner.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.Reward.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.SynchronizeHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
 | 
			
		||||
	syncedClaim, found := k.GetHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove claimed coins (NOT reward coins)
 | 
			
		||||
	syncedClaim.Reward = syncedClaim.Reward.Sub(claimingCoins)
 | 
			
		||||
	k.SetHardLiquidityProviderClaim(ctx, syncedClaim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, owner.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claimingCoins.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, syncedClaim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	claim, found := k.GetDelegatorClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syncedClaim, err := k.SynchronizeDelegatorClaim(ctx, claim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove claimed coins (NOT reward coins)
 | 
			
		||||
	syncedClaim.Reward = syncedClaim.Reward.Sub(claimingCoins)
 | 
			
		||||
	k.SetDelegatorClaim(ctx, syncedClaim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, owner.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claimingCoins.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, syncedClaim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syncedClaim, found := k.GetSynchronizedSwapClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimingCoins := types.FilterCoins(syncedClaim.Reward, denomsToClaim)
 | 
			
		||||
	rewardCoins := types.MultiplyCoins(claimingCoins, multiplier.Factor)
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove claimed coins (NOT reward coins)
 | 
			
		||||
	syncedClaim.Reward = syncedClaim.Reward.Sub(claimingCoins)
 | 
			
		||||
	k.SetSwapClaim(ctx, syncedClaim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, owner.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claimingCoins.String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, syncedClaim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (k Keeper) ValidateIsValidatorVestingAccount(ctx sdk.Context, address sdk.AccAddress) error {
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, address)
 | 
			
		||||
	if acc == nil {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrAccountNotFound, "address not found: %s", address)
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := acc.(*validatorvesting.ValidatorVestingAccount)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "account is not validator vesting account, %s", address)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										81
									
								
								x/incentive/keeper/claim_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								x/incentive/keeper/claim_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
package keeper_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ClaimTests runs unit tests for the keeper Claim methods
 | 
			
		||||
type ClaimTests struct {
 | 
			
		||||
	unitTester
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestClaim(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(ClaimTests))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ClaimTests) ErrorIs(err, target error) bool {
 | 
			
		||||
	return suite.Truef(errors.Is(err, target), "err didn't match: %s, it was: %s", target, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ClaimTests) TestCannotClaimWhenMultiplierNotRecognised() {
 | 
			
		||||
	subspace := &fakeParamSubspace{
 | 
			
		||||
		params: types.Params{
 | 
			
		||||
			ClaimMultipliers: types.Multipliers{
 | 
			
		||||
				types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.keeper = suite.NewKeeper(subspace, nil, nil, nil, nil, nil, nil)
 | 
			
		||||
 | 
			
		||||
	claim := types.DelegatorClaim{
 | 
			
		||||
		BaseMultiClaim: types.BaseMultiClaim{
 | 
			
		||||
			Owner: arbitraryAddress(),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.storeDelegatorClaim(claim)
 | 
			
		||||
 | 
			
		||||
	// multiplier not in params
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, types.Large, nil)
 | 
			
		||||
	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)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrInvalidMultiplier)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ClaimTests) TestCannotClaimAfterEndTime() {
 | 
			
		||||
	endTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)
 | 
			
		||||
 | 
			
		||||
	subspace := &fakeParamSubspace{
 | 
			
		||||
		params: types.Params{
 | 
			
		||||
			ClaimMultipliers: types.Multipliers{
 | 
			
		||||
				types.NewMultiplier(types.Small, 1, d("0.2")),
 | 
			
		||||
			},
 | 
			
		||||
			ClaimEnd: endTime,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.keeper = suite.NewKeeper(subspace, nil, nil, nil, nil, nil, nil)
 | 
			
		||||
 | 
			
		||||
	suite.ctx = suite.ctx.WithBlockTime(endTime.Add(time.Nanosecond))
 | 
			
		||||
 | 
			
		||||
	claim := types.DelegatorClaim{
 | 
			
		||||
		BaseMultiClaim: types.BaseMultiClaim{
 | 
			
		||||
			Owner: arbitraryAddress(),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.storeDelegatorClaim(claim)
 | 
			
		||||
 | 
			
		||||
	err := suite.keeper.ClaimDelegatorReward(suite.ctx, claim.Owner, claim.Owner, types.Small, nil)
 | 
			
		||||
	suite.ErrorIs(err, types.ErrClaimExpired)
 | 
			
		||||
}
 | 
			
		||||
@ -9,16 +9,10 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/cdp"
 | 
			
		||||
	committeetypes "github.com/kava-labs/kava/x/committee/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	hardtypes "github.com/kava-labs/kava/x/hard/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/pricefeed"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	oneYear time.Duration = time.Hour * 24 * 365
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Avoid cluttering test cases with long function names
 | 
			
		||||
func i(in int64) sdk.Int                    { return sdk.NewInt(in) }
 | 
			
		||||
func d(str string) sdk.Dec                  { return sdk.MustNewDecFromStr(str) }
 | 
			
		||||
@ -168,19 +162,19 @@ func NewPricefeedGenStateMultiFromTime(t time.Time) app.GenesisState {
 | 
			
		||||
	return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHardGenStateMulti(genTime time.Time) HardGenesisBuilder {
 | 
			
		||||
	kavaMM := NewStandardMoneyMarket("ukava")
 | 
			
		||||
func NewHardGenStateMulti(genTime time.Time) testutil.HardGenesisBuilder {
 | 
			
		||||
	kavaMM := testutil.NewStandardMoneyMarket("ukava")
 | 
			
		||||
	kavaMM.SpotMarketID = "kava:usd"
 | 
			
		||||
	btcMM := NewStandardMoneyMarket("btcb")
 | 
			
		||||
	btcMM := testutil.NewStandardMoneyMarket("btcb")
 | 
			
		||||
	btcMM.SpotMarketID = "btc:usd"
 | 
			
		||||
 | 
			
		||||
	builder := NewHardGenesisBuilder().WithGenesisTime(genTime).
 | 
			
		||||
		WithInitializedMoneyMarket(NewStandardMoneyMarket("usdx")).
 | 
			
		||||
	builder := testutil.NewHardGenesisBuilder().WithGenesisTime(genTime).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("usdx")).
 | 
			
		||||
		WithInitializedMoneyMarket(kavaMM).
 | 
			
		||||
		WithInitializedMoneyMarket(NewStandardMoneyMarket("bnb")).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("bnb")).
 | 
			
		||||
		WithInitializedMoneyMarket(btcMM).
 | 
			
		||||
		WithInitializedMoneyMarket(NewStandardMoneyMarket("xrp")).
 | 
			
		||||
		WithInitializedMoneyMarket(NewStandardMoneyMarket("zzz"))
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("xrp")).
 | 
			
		||||
		WithInitializedMoneyMarket(testutil.NewStandardMoneyMarket("zzz"))
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -212,162 +206,3 @@ func NewCommitteeGenesisState(members []sdk.AccAddress) app.GenesisState {
 | 
			
		||||
		committeetypes.ModuleName: committeetypes.ModuleCdc.MustMarshalJSON(genState),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncentiveGenesisBuilder is a tool for creating an incentive genesis state.
 | 
			
		||||
// Helper methods add values onto a default genesis state.
 | 
			
		||||
// All methods are immutable and return updated copies of the builder.
 | 
			
		||||
type IncentiveGenesisBuilder struct {
 | 
			
		||||
	types.GenesisState
 | 
			
		||||
	genesisTime time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewIncentiveGenesisBuilder() IncentiveGenesisBuilder {
 | 
			
		||||
	return IncentiveGenesisBuilder{
 | 
			
		||||
		GenesisState: types.DefaultGenesisState(),
 | 
			
		||||
		genesisTime:  time.Time{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) Build() types.GenesisState {
 | 
			
		||||
	return builder.GenesisState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) BuildMarshalled() app.GenesisState {
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		types.ModuleName: types.ModuleCdc.MustMarshalJSON(builder.Build()),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithGenesisTime(time time.Time) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.genesisTime = time
 | 
			
		||||
	builder.Params.ClaimEnd = time.Add(5 * oneYear)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedBorrowRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.HardBorrowRewardPeriods = append(builder.Params.HardBorrowRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.HardBorrowAccumulationTimes = append(builder.HardBorrowAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleBorrowRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedBorrowRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedSupplyRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.HardSupplyRewardPeriods = append(builder.Params.HardSupplyRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.HardSupplyAccumulationTimes = append(builder.HardSupplyAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleSupplyRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedSupplyRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.DelegatorRewardPeriods = append(builder.Params.DelegatorRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.DelegatorAccumulationTimes = append(builder.DelegatorAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleDelegatorRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedDelegatorRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedUSDXRewardPeriod(period types.RewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.USDXMintingRewardPeriods = append(builder.Params.USDXMintingRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.USDXAccumulationTimes = append(builder.USDXAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleUSDXRewardPeriod(ctype string, rewardsPerSecond sdk.Coin) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedUSDXRewardPeriod(types.NewRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithMultipliers(multipliers types.Multipliers) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.ClaimMultipliers = multipliers
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HardGenesisBuilder is a tool for creating a hard genesis state.
 | 
			
		||||
// Helper methods add values onto a default genesis state.
 | 
			
		||||
// All methods are immutable and return updated copies of the builder.
 | 
			
		||||
type HardGenesisBuilder struct {
 | 
			
		||||
	hardtypes.GenesisState
 | 
			
		||||
	genesisTime time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHardGenesisBuilder() HardGenesisBuilder {
 | 
			
		||||
	return HardGenesisBuilder{
 | 
			
		||||
		GenesisState: hardtypes.DefaultGenesisState(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) Build() hardtypes.GenesisState {
 | 
			
		||||
	return builder.GenesisState
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) BuildMarshalled() app.GenesisState {
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		hardtypes.ModuleName: hardtypes.ModuleCdc.MustMarshalJSON(builder.Build()),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithGenesisTime(genTime time.Time) HardGenesisBuilder {
 | 
			
		||||
	builder.genesisTime = genTime
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithInitializedMoneyMarket(market hard.MoneyMarket) HardGenesisBuilder {
 | 
			
		||||
	builder.Params.MoneyMarkets = append(builder.Params.MoneyMarkets, market)
 | 
			
		||||
 | 
			
		||||
	builder.PreviousAccumulationTimes = append(
 | 
			
		||||
		builder.PreviousAccumulationTimes,
 | 
			
		||||
		hardtypes.NewGenesisAccumulationTime(market.Denom, builder.genesisTime, sdk.OneDec(), sdk.OneDec()),
 | 
			
		||||
	)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithMinBorrow(minUSDValue sdk.Dec) HardGenesisBuilder {
 | 
			
		||||
	builder.Params.MinimumBorrowUSDValue = minUSDValue
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func NewStandardMoneyMarket(denom string) hardtypes.MoneyMarket {
 | 
			
		||||
	return hardtypes.NewMoneyMarket(
 | 
			
		||||
		denom,
 | 
			
		||||
		hard.NewBorrowLimit(
 | 
			
		||||
			false,
 | 
			
		||||
			sdk.NewDec(1e15),
 | 
			
		||||
			d("0.6"),
 | 
			
		||||
		),
 | 
			
		||||
		denom+":usd",
 | 
			
		||||
		i(1e6),
 | 
			
		||||
		hard.NewInterestRateModel(d("0.05"), d("2"), d("0.8"), d("10")),
 | 
			
		||||
		d("0.05"),
 | 
			
		||||
		sdk.ZeroDec(),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,367 +23,6 @@ const (
 | 
			
		||||
	PaymentHour = 14
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ClaimUSDXMintingReward sends the reward amount to the input address and zero's out the claim in the store
 | 
			
		||||
func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	claim, found := k.GetUSDXMintingClaim(ctx, addr)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claim, err := k.SynchronizeUSDXMintingClaim(ctx, claim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rewardAmount := claim.Reward.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
	if rewardAmount.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount)
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(rewardCoin), length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroUSDXMintingClaim(ctx, claim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, claim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimUSDXMintingReward sends the reward amount to the input receiver address and zero's out the claim in the store
 | 
			
		||||
func (k Keeper) ClaimUSDXMintingRewardVVesting(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	claim, found := k.GetUSDXMintingClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, owner)
 | 
			
		||||
	if acc == nil {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrAccountNotFound, "address not found: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := acc.(*validatorvesting.ValidatorVestingAccount)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "owner account must be validator vesting account %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claim, err := k.SynchronizeUSDXMintingClaim(ctx, claim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rewardAmount := claim.Reward.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
	if rewardAmount.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount)
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, sdk.NewCoins(rewardCoin), length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroUSDXMintingClaim(ctx, claim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, claim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimHardReward sends the reward amount to the input address and zero's out the claim in the store
 | 
			
		||||
func (k Keeper) ClaimHardReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	_, found := k.GetHardLiquidityProviderClaim(ctx, addr)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.SynchronizeHardLiquidityProviderClaim(ctx, addr)
 | 
			
		||||
 | 
			
		||||
	claim, found := k.GetHardLiquidityProviderClaim(ctx, addr)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rewardCoins sdk.Coins
 | 
			
		||||
	for _, coin := range claim.Reward {
 | 
			
		||||
		rewardAmount := coin.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
		if rewardAmount.IsZero() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount))
 | 
			
		||||
	}
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroHardLiquidityProviderClaim(ctx, claim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, claim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, claim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimHardRewardVVesting sends the reward amount to the input address and zero's out the claim in the store
 | 
			
		||||
func (k Keeper) ClaimHardRewardVVesting(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	_, found := k.GetHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, owner)
 | 
			
		||||
	if acc == nil {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrAccountNotFound, "address not found: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := acc.(*validatorvesting.ValidatorVestingAccount)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "owner account must be validator vesting account %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.SynchronizeHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
 | 
			
		||||
	claim, found := k.GetHardLiquidityProviderClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rewardCoins sdk.Coins
 | 
			
		||||
	for _, coin := range claim.Reward {
 | 
			
		||||
		rewardAmount := coin.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
		if rewardAmount.IsZero() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount))
 | 
			
		||||
	}
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroHardLiquidityProviderClaim(ctx, claim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, claim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, claim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimDelegatorReward sends the reward amount to the input address and zero's out the delegator claim in the store
 | 
			
		||||
func (k Keeper) ClaimDelegatorReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	claim, found := k.GetDelegatorClaim(ctx, addr)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syncedClaim, err := k.SynchronizeDelegatorClaim(ctx, claim)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rewardCoins sdk.Coins
 | 
			
		||||
	for _, coin := range syncedClaim.Reward {
 | 
			
		||||
		rewardAmount := coin.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
		if rewardAmount.IsZero() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount))
 | 
			
		||||
	}
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroDelegatorClaim(ctx, syncedClaim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, syncedClaim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, syncedClaim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, syncedClaim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClaimDelegatorRewardVVesting sends the reward amount to the input address and zero's out the claim in the store
 | 
			
		||||
func (k Keeper) ClaimDelegatorRewardVVesting(ctx sdk.Context, owner, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
 | 
			
		||||
	claim, found := k.GetDelegatorClaim(ctx, owner)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acc := k.accountKeeper.GetAccount(ctx, owner)
 | 
			
		||||
	if acc == nil {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrAccountNotFound, "address not found: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := acc.(*validatorvesting.ValidatorVestingAccount)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidAccountType, "owner account must be validator vesting account %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	multiplier, found := k.GetMultiplier(ctx, multiplierName)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claimEnd := k.GetClaimEnd(ctx)
 | 
			
		||||
 | 
			
		||||
	if ctx.BlockTime().After(claimEnd) {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syncedClaim, err := k.SynchronizeDelegatorClaim(ctx, claim)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", owner)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rewardCoins sdk.Coins
 | 
			
		||||
	for _, coin := range syncedClaim.Reward {
 | 
			
		||||
		rewardAmount := coin.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
 | 
			
		||||
		if rewardAmount.IsZero() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount))
 | 
			
		||||
	}
 | 
			
		||||
	if rewardCoins.IsZero() {
 | 
			
		||||
		return types.ErrZeroClaim
 | 
			
		||||
	}
 | 
			
		||||
	length, err := k.GetPeriodLength(ctx, multiplier)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, receiver, rewardCoins, length)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.ZeroDelegatorClaim(ctx, syncedClaim)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeClaim,
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimedBy, syncedClaim.GetOwner().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimAmount, syncedClaim.GetReward().String()),
 | 
			
		||||
			sdk.NewAttribute(types.AttributeKeyClaimType, syncedClaim.GetType()),
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendTimeLockedCoinsToAccount sends time-locked coins from the input module account to the recipient. If the recipients account is not a vesting account and the input length is greater than zero, the recipient account is converted to a periodic vesting account and the coins are added to the vesting balance as a vesting period with the input length.
 | 
			
		||||
func (k Keeper) SendTimeLockedCoinsToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, length int64) error {
 | 
			
		||||
	macc := k.supplyKeeper.GetModuleAccount(ctx, senderModule)
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
@ -18,12 +17,11 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
 | 
			
		||||
	"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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Test suite used for all keeper tests
 | 
			
		||||
@ -61,7 +59,7 @@ func (suite *PayoutTestSuite) SetupApp() {
 | 
			
		||||
	suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
 | 
			
		||||
func (suite *PayoutTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder testutil.IncentiveGenesisBuilder, hardBuilder testutil.HardGenesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	suite.app.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
@ -84,674 +82,6 @@ func (suite *PayoutTestSuite) getModuleAccount(name string) supplyexported.Modul
 | 
			
		||||
	return sk.GetModuleAccount(suite.ctx, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) TestPayoutUSDXMintingClaim() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		ctype                    string
 | 
			
		||||
		rewardsPerSecond         sdk.Coin
 | 
			
		||||
		initialCollateral        sdk.Coin
 | 
			
		||||
		initialPrincipal         sdk.Coin
 | 
			
		||||
		multipliers              types.Multipliers
 | 
			
		||||
		multiplier               types.MultiplierName
 | 
			
		||||
		timeElapsed              int
 | 
			
		||||
		expectedBalance          sdk.Coins
 | 
			
		||||
		expectedPeriods          vesting.Periods
 | 
			
		||||
		isPeriodicVestingAccount bool
 | 
			
		||||
	}
 | 
			
		||||
	type errArgs struct {
 | 
			
		||||
		expectPass bool
 | 
			
		||||
		contains   string
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		errArgs errArgs
 | 
			
		||||
	}
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				ctype:                    "bnb-a",
 | 
			
		||||
				rewardsPerSecond:         c("ukava", 122354),
 | 
			
		||||
				initialCollateral:        c("bnb", 1000000000000),
 | 
			
		||||
				initialPrincipal:         c("usdx", 10000000000),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedBalance:          cs(c("usdx", 10000000000), c("ukava", 10571385600)),
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"invalid zero rewards",
 | 
			
		||||
			args{
 | 
			
		||||
				ctype:                    "bnb-a",
 | 
			
		||||
				rewardsPerSecond:         c("ukava", 0),
 | 
			
		||||
				initialCollateral:        c("bnb", 1000000000000),
 | 
			
		||||
				initialPrincipal:         c("usdx", 10000000000),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedBalance:          cs(c("usdx", 10000000000)),
 | 
			
		||||
				expectedPeriods:          vesting.Periods{},
 | 
			
		||||
				isPeriodicVestingAccount: false,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: false,
 | 
			
		||||
				contains:   "claim amount rounds to zero",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			userAddr := suite.addrs[0]
 | 
			
		||||
			authBulder := app.NewAuthGenesisBuilder().
 | 
			
		||||
				WithSimpleAccount(userAddr, cs(tc.args.initialCollateral)).
 | 
			
		||||
				WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 1000000000000)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond).
 | 
			
		||||
				WithMultipliers(tc.args.multipliers)
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
 | 
			
		||||
 | 
			
		||||
			// setup cdp state
 | 
			
		||||
			err := suite.cdpKeeper.AddCdp(suite.ctx, userAddr, tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
 | 
			
		||||
 | 
			
		||||
			updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
 | 
			
		||||
			suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
 | 
			
		||||
			rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			err = suite.keeper.ClaimUSDXMintingReward(suite.ctx, userAddr, tc.args.multiplier)
 | 
			
		||||
 | 
			
		||||
			if tc.errArgs.expectPass {
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
				acc := suite.getAccount(userAddr)
 | 
			
		||||
				suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins())
 | 
			
		||||
 | 
			
		||||
				if tc.args.isPeriodicVestingAccount {
 | 
			
		||||
					vacc, ok := acc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
					suite.Require().True(ok)
 | 
			
		||||
					suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, userAddr)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				suite.Require().Equal(c("ukava", 0), claim.Reward)
 | 
			
		||||
			} else {
 | 
			
		||||
				suite.Require().Error(err)
 | 
			
		||||
				suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) TestPayoutUSDXMintingClaimVVesting() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		ctype             string
 | 
			
		||||
		rewardsPerSecond  sdk.Coin
 | 
			
		||||
		initialCollateral sdk.Coin
 | 
			
		||||
		initialPrincipal  sdk.Coin
 | 
			
		||||
		multipliers       types.Multipliers
 | 
			
		||||
		multiplier        types.MultiplierName
 | 
			
		||||
		timeElapsed       int
 | 
			
		||||
		expectedBalance   sdk.Coins
 | 
			
		||||
		expectedPeriods   vesting.Periods
 | 
			
		||||
	}
 | 
			
		||||
	type errArgs struct {
 | 
			
		||||
		expectPass bool
 | 
			
		||||
		contains   string
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		errArgs errArgs
 | 
			
		||||
	}
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				ctype:             "bnb-a",
 | 
			
		||||
				rewardsPerSecond:  c("ukava", 122354),
 | 
			
		||||
				initialCollateral: c("bnb", 1e12),
 | 
			
		||||
				initialPrincipal:  c("usdx", 1e10),
 | 
			
		||||
				multipliers:       types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:        types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:       86400,
 | 
			
		||||
				expectedBalance:   cs(c("ukava", 10571385600)),
 | 
			
		||||
				expectedPeriods:   vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"invalid zero rewards",
 | 
			
		||||
			args{
 | 
			
		||||
				ctype:             "bnb-a",
 | 
			
		||||
				rewardsPerSecond:  c("ukava", 0),
 | 
			
		||||
				initialCollateral: c("bnb", 1e12),
 | 
			
		||||
				initialPrincipal:  c("usdx", 1e10),
 | 
			
		||||
				multipliers:       types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:        types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:       86400,
 | 
			
		||||
				expectedBalance:   cs(),
 | 
			
		||||
				expectedPeriods:   vesting.Periods{},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: false,
 | 
			
		||||
				contains:   "claim amount rounds to zero",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
 | 
			
		||||
			bacc := auth.NewBaseAccount(suite.addrs[2], cs(tc.args.initialCollateral, c("ukava", 400)), nil, 0, 0)
 | 
			
		||||
			bva, err := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.genesisTime.Unix()+16)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			periods := vesting.Periods{
 | 
			
		||||
				vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
 | 
			
		||||
			}
 | 
			
		||||
			vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.genesisTime.Unix(), periods, sdk.ConsAddress{}, nil, 90)
 | 
			
		||||
 | 
			
		||||
			authBulder := app.NewAuthGenesisBuilder().
 | 
			
		||||
				WithAccounts(vva).
 | 
			
		||||
				WithSimpleModuleAccount(kavadist.ModuleName, cs(c("ukava", 1e18))).
 | 
			
		||||
				WithSimpleAccount(suite.addrs[0], cs()) // the recipient address needs to be a instantiated account // TODO change?
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond).
 | 
			
		||||
				WithMultipliers(tc.args.multipliers)
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
 | 
			
		||||
 | 
			
		||||
			// setup cdp state
 | 
			
		||||
			cdpKeeper := suite.app.GetCDPKeeper()
 | 
			
		||||
			err = cdpKeeper.AddCdp(suite.ctx, suite.addrs[2], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[2])
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
 | 
			
		||||
 | 
			
		||||
			// accumulate some usdx rewards
 | 
			
		||||
			updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
 | 
			
		||||
			suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
 | 
			
		||||
			rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			err = suite.keeper.ClaimUSDXMintingRewardVVesting(suite.ctx, suite.addrs[2], suite.addrs[0], tc.args.multiplier)
 | 
			
		||||
 | 
			
		||||
			if tc.errArgs.expectPass {
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
				ak := suite.app.GetAccountKeeper()
 | 
			
		||||
				acc := ak.GetAccount(suite.ctx, suite.addrs[0])
 | 
			
		||||
				suite.Require().Equal(tc.args.expectedBalance, acc.GetCoins())
 | 
			
		||||
 | 
			
		||||
				vacc, ok := acc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
				suite.Require().True(ok)
 | 
			
		||||
				suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
 | 
			
		||||
 | 
			
		||||
				claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[2])
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				suite.Require().Equal(c("ukava", 0), claim.Reward)
 | 
			
		||||
			} else {
 | 
			
		||||
				suite.Require().Error(err)
 | 
			
		||||
				suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) TestPayoutHardLiquidityProviderClaim() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		deposit                  sdk.Coins
 | 
			
		||||
		borrow                   sdk.Coins
 | 
			
		||||
		rewardsPerSecond         sdk.Coins
 | 
			
		||||
		multipliers              types.Multipliers
 | 
			
		||||
		multiplier               types.MultiplierName
 | 
			
		||||
		timeElapsed              int64
 | 
			
		||||
		expectedRewards          sdk.Coins
 | 
			
		||||
		expectedPeriods          vesting.Periods
 | 
			
		||||
		isPeriodicVestingAccount bool
 | 
			
		||||
	}
 | 
			
		||||
	type errArgs struct {
 | 
			
		||||
		expectPass bool
 | 
			
		||||
		contains   string
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		errArgs errArgs
 | 
			
		||||
	}
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"single reward denom: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:                  cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:                   cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond:         cs(c("hard", 122354)),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedRewards:          cs(c("hard", 21142771200)), // 10571385600 (deposit reward) + 10571385600 (borrow reward)
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"single reward denom: valid 10 days",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:                  cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:                   cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond:         cs(c("hard", 122354)),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              864000,
 | 
			
		||||
				expectedRewards:          cs(c("hard", 211427712000)), // 105713856000 (deposit reward) + 105713856000 (borrow reward)
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712000))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:                  cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:                   cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond:         cs(c("hard", 122354), c("ukava", 122354)),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedRewards:          cs(c("hard", 21142771200), c("ukava", 21142771200)), // 10571385600 (deposit reward) + 10571385600 (borrow reward)
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200), c("ukava", 21142771200))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms: valid 10 days",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:                  cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:                   cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond:         cs(c("hard", 122354), c("ukava", 122354)),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              864000,
 | 
			
		||||
				expectedRewards:          cs(c("hard", 211427712000), c("ukava", 211427712000)), // 105713856000 (deposit reward) + 105713856000 (borrow reward)
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712000), c("ukava", 211427712000))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms with different rewards per second: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:                  cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:                   cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond:         cs(c("hard", 122354), c("ukava", 222222)),
 | 
			
		||||
				multipliers:              types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:               types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:              86400,
 | 
			
		||||
				expectedRewards:          cs(c("hard", 21142771200), c("ukava", 38399961600)),
 | 
			
		||||
				expectedPeriods:          vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771200), c("ukava", 38399961600))}},
 | 
			
		||||
				isPeriodicVestingAccount: true,
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			userAddr := suite.addrs[3]
 | 
			
		||||
			authBulder := app.NewAuthGenesisBuilder().
 | 
			
		||||
				WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15))).
 | 
			
		||||
				WithSimpleModuleAccount(kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithMultipliers(tc.args.multipliers)
 | 
			
		||||
			for _, c := range tc.args.deposit {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
			for _, c := range tc.args.borrow {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
 | 
			
		||||
 | 
			
		||||
			// User deposits and borrows
 | 
			
		||||
			err := suite.hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			err = suite.hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			// Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards
 | 
			
		||||
			claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			for _, coin := range tc.args.deposit {
 | 
			
		||||
				suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Set up future runtime context
 | 
			
		||||
			runAtTime := time.Unix(suite.ctx.BlockTime().Unix()+(tc.args.timeElapsed), 0)
 | 
			
		||||
			runCtx := suite.ctx.WithBlockTime(runAtTime)
 | 
			
		||||
 | 
			
		||||
			// Accumulate supply rewards for each deposit denom
 | 
			
		||||
			for _, coin := range tc.args.deposit {
 | 
			
		||||
				rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriods(runCtx, coin.Denom)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				err = suite.keeper.AccumulateHardSupplyRewards(runCtx, rewardPeriod)
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Accumulate borrow rewards for each deposit denom
 | 
			
		||||
			for _, coin := range tc.args.borrow {
 | 
			
		||||
				rewardPeriod, found := suite.keeper.GetHardBorrowRewardPeriods(runCtx, coin.Denom)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				err = suite.keeper.AccumulateHardBorrowRewards(runCtx, rewardPeriod)
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Sync hard supply rewards
 | 
			
		||||
			deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
 | 
			
		||||
 | 
			
		||||
			// Sync hard borrow rewards
 | 
			
		||||
			borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
 | 
			
		||||
 | 
			
		||||
			// Fetch pre-claim balances
 | 
			
		||||
			ak := suite.app.GetAccountKeeper()
 | 
			
		||||
			preClaimAcc := ak.GetAccount(runCtx, userAddr)
 | 
			
		||||
 | 
			
		||||
			err = suite.keeper.ClaimHardReward(runCtx, userAddr, tc.args.multiplier)
 | 
			
		||||
			if tc.errArgs.expectPass {
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
				// Check that user's balance has increased by expected reward amount
 | 
			
		||||
				postClaimAcc := ak.GetAccount(suite.ctx, userAddr)
 | 
			
		||||
				suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins())
 | 
			
		||||
 | 
			
		||||
				if tc.args.isPeriodicVestingAccount {
 | 
			
		||||
					vacc, ok := postClaimAcc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
					suite.Require().True(ok)
 | 
			
		||||
					suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
				claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, userAddr)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				for _, claimRewardCoin := range claim.Reward {
 | 
			
		||||
					suite.Require().Equal(c(claimRewardCoin.Denom, 0), claimRewardCoin)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				suite.Require().Error(err)
 | 
			
		||||
				suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) TestPayoutHardLiquidityProviderClaimVVesting() {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		deposit          sdk.Coins
 | 
			
		||||
		borrow           sdk.Coins
 | 
			
		||||
		rewardsPerSecond sdk.Coins
 | 
			
		||||
		multipliers      types.Multipliers
 | 
			
		||||
		multiplier       types.MultiplierName
 | 
			
		||||
		timeElapsed      int64
 | 
			
		||||
		expectedRewards  sdk.Coins
 | 
			
		||||
		expectedPeriods  vesting.Periods
 | 
			
		||||
	}
 | 
			
		||||
	type errArgs struct {
 | 
			
		||||
		expectPass bool
 | 
			
		||||
		contains   string
 | 
			
		||||
	}
 | 
			
		||||
	type test struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		errArgs errArgs
 | 
			
		||||
	}
 | 
			
		||||
	testCases := []test{
 | 
			
		||||
		{
 | 
			
		||||
			"single reward denom: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:          cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:           cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond: cs(c("hard", 122354)),
 | 
			
		||||
				multipliers:      types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:       types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:      86400,
 | 
			
		||||
				expectedRewards:  cs(c("hard", 21142771202)),
 | 
			
		||||
				expectedPeriods:  vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"single reward denom: valid 10 days",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:          cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:           cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond: cs(c("hard", 122354)),
 | 
			
		||||
				multipliers:      types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:       types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:      864000,
 | 
			
		||||
				expectedRewards:  cs(c("hard", 211427712008)),
 | 
			
		||||
				expectedPeriods:  vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712008))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:          cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:           cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
 | 
			
		||||
				multipliers:      types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:       types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:      86400,
 | 
			
		||||
				expectedRewards:  cs(c("hard", 21142771202), c("ukava", 21142771202)),
 | 
			
		||||
				expectedPeriods:  vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202), c("ukava", 21142771202))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms: valid 10 days",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:          cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:           cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond: cs(c("hard", 122354), c("ukava", 122354)),
 | 
			
		||||
				multipliers:      types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:       types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:      864000,
 | 
			
		||||
				expectedRewards:  cs(c("hard", 211427712008), c("ukava", 211427712008)),
 | 
			
		||||
				expectedPeriods:  vesting.Periods{vesting.Period{Length: 32140800, Amount: cs(c("hard", 211427712008), c("ukava", 211427712008))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"multiple reward denoms with different rewards per second: valid 1 day",
 | 
			
		||||
			args{
 | 
			
		||||
				deposit:          cs(c("bnb", 10000000000)),
 | 
			
		||||
				borrow:           cs(c("bnb", 5000000000)),
 | 
			
		||||
				rewardsPerSecond: cs(c("hard", 122354), c("ukava", 222222)),
 | 
			
		||||
				multipliers:      types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
 | 
			
		||||
				multiplier:       types.MultiplierName("large"),
 | 
			
		||||
				timeElapsed:      86400,
 | 
			
		||||
				expectedRewards:  cs(c("hard", 21142771202), c("ukava", 38399961603)),
 | 
			
		||||
				expectedPeriods:  vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("hard", 21142771202), c("ukava", 38399961603))}},
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: true,
 | 
			
		||||
				contains:   "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
 | 
			
		||||
			userAddr := suite.addrs[3]
 | 
			
		||||
 | 
			
		||||
			bacc := auth.NewBaseAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)), nil, 0, 0)
 | 
			
		||||
			bva, err := vesting.NewBaseVestingAccount(bacc, cs(c("ukava", 400)), suite.genesisTime.Unix()+16)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			periods := vesting.Periods{
 | 
			
		||||
				vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
 | 
			
		||||
				vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
 | 
			
		||||
			}
 | 
			
		||||
			vva := validatorvesting.NewValidatorVestingAccountRaw(bva, suite.genesisTime.Unix(), periods, sdk.ConsAddress{}, nil, 90)
 | 
			
		||||
 | 
			
		||||
			authBulder := app.NewAuthGenesisBuilder().
 | 
			
		||||
				WithAccounts(vva).
 | 
			
		||||
				WithSimpleAccount(suite.addrs[2], cs()).
 | 
			
		||||
				WithSimpleModuleAccount(kavadist.ModuleName, cs(c("hard", 1000000000000000000), c("ukava", 1000000000000000000)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithMultipliers(tc.args.multipliers)
 | 
			
		||||
			for _, c := range tc.args.deposit {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
			for _, c := range tc.args.borrow {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(c.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBulder, incentBuilder, NewHardGenStateMulti(suite.genesisTime))
 | 
			
		||||
 | 
			
		||||
			ak := suite.app.GetAccountKeeper()
 | 
			
		||||
			hardKeeper := suite.app.GetHardKeeper()
 | 
			
		||||
 | 
			
		||||
			// User deposits and borrows
 | 
			
		||||
			err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.borrow)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			// Check that Hard hooks initialized a HardLiquidityProviderClaim that has 0 rewards
 | 
			
		||||
			claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			for _, coin := range tc.args.deposit {
 | 
			
		||||
				suite.Require().Equal(sdk.ZeroInt(), claim.Reward.AmountOf(coin.Denom))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Set up future runtime context
 | 
			
		||||
			runAtTime := time.Unix(suite.ctx.BlockTime().Unix()+(tc.args.timeElapsed), 0)
 | 
			
		||||
			runCtx := suite.ctx.WithBlockTime(runAtTime)
 | 
			
		||||
 | 
			
		||||
			// Run Hard begin blocker
 | 
			
		||||
			hard.BeginBlocker(runCtx, suite.hardKeeper)
 | 
			
		||||
 | 
			
		||||
			// Accumulate supply rewards for each deposit denom
 | 
			
		||||
			for _, coin := range tc.args.deposit {
 | 
			
		||||
				rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriods(runCtx, coin.Denom)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				err = suite.keeper.AccumulateHardSupplyRewards(runCtx, rewardPeriod)
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Accumulate borrow rewards for each deposit denom
 | 
			
		||||
			for _, coin := range tc.args.borrow {
 | 
			
		||||
				rewardPeriod, found := suite.keeper.GetHardBorrowRewardPeriods(runCtx, coin.Denom)
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				err = suite.keeper.AccumulateHardBorrowRewards(runCtx, rewardPeriod)
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Sync hard supply rewards
 | 
			
		||||
			deposit, found := suite.hardKeeper.GetDeposit(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
 | 
			
		||||
 | 
			
		||||
			// Sync hard borrow rewards
 | 
			
		||||
			borrow, found := suite.hardKeeper.GetBorrow(suite.ctx, userAddr)
 | 
			
		||||
			suite.Require().True(found)
 | 
			
		||||
			suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
 | 
			
		||||
 | 
			
		||||
			// Fetch pre-claim balances
 | 
			
		||||
			preClaimAcc := ak.GetAccount(runCtx, suite.addrs[2])
 | 
			
		||||
 | 
			
		||||
			err = suite.keeper.ClaimHardRewardVVesting(runCtx, userAddr, suite.addrs[2], tc.args.multiplier)
 | 
			
		||||
			if tc.errArgs.expectPass {
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
				// Check that user's balance has increased by expected reward amount
 | 
			
		||||
				postClaimAcc := ak.GetAccount(suite.ctx, suite.addrs[2])
 | 
			
		||||
				suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedRewards...), postClaimAcc.GetCoins())
 | 
			
		||||
 | 
			
		||||
				vacc, ok := postClaimAcc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
				suite.Require().True(ok)
 | 
			
		||||
				suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
 | 
			
		||||
 | 
			
		||||
				// Check that each claim reward coin's amount has been reset to 0
 | 
			
		||||
				claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, suite.addrs[3])
 | 
			
		||||
				suite.Require().True(found)
 | 
			
		||||
				for _, claimRewardCoin := range claim.Reward {
 | 
			
		||||
					suite.Require().Equal(c(claimRewardCoin.Denom, 0), claimRewardCoin)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				suite.Require().Error(err)
 | 
			
		||||
				suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *PayoutTestSuite) TestSendCoinsToPeriodicVestingAccount() {
 | 
			
		||||
	type accountArgs struct {
 | 
			
		||||
		periods          vesting.Periods
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -53,7 +54,7 @@ func (suite *BorrowRewardsTestSuite) SetupApp() {
 | 
			
		||||
	suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *BorrowRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
 | 
			
		||||
func (suite *BorrowRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder testutil.IncentiveGenesisBuilder, hardBuilder testutil.HardGenesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	suite.app.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
@ -162,7 +163,7 @@ func (suite *BorrowRewardsTestSuite) TestAccumulateHardBorrowRewards() {
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleBorrowRewardPeriod(tc.args.borrow.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -317,7 +318,7 @@ func (suite *BorrowRewardsTestSuite) TestInitializeHardBorrowRewards() {
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			for moneyMarketDenom, rewardsPerSecond := range tc.args.moneyMarketRewardDenoms {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(moneyMarketDenom, rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
@ -506,7 +507,7 @@ func (suite *BorrowRewardsTestSuite) TestSynchronizeHardBorrowReward() {
 | 
			
		||||
				WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
 | 
			
		||||
				WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			if tc.args.rewardsPerSecond != nil {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleBorrowRewardPeriod(tc.args.incentiveBorrowRewardDenom, tc.args.rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
@ -833,7 +834,7 @@ func (suite *BorrowRewardsTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
			
		||||
					cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
				)
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleBorrowRewardPeriod("bnb", tc.args.rewardsPerSecond).
 | 
			
		||||
				WithSimpleBorrowRewardPeriod("ukava", tc.args.rewardsPerSecond).
 | 
			
		||||
@ -935,7 +936,7 @@ func (suite *BorrowRewardsTestSuite) TestSimulateHardBorrowRewardSynchronization
 | 
			
		||||
			userAddr := suite.addrs[3]
 | 
			
		||||
			authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleBorrowRewardPeriod(tc.args.borrow.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -172,10 +172,7 @@ func (suite *SynchronizeDelegatorRewardTests) TestRewardIsIncreasedWhenNewReward
 | 
			
		||||
 | 
			
		||||
	suite.Equal(newGlobalIndexes, syncedClaim.RewardIndexes)
 | 
			
		||||
	suite.Equal(
 | 
			
		||||
		cs(
 | 
			
		||||
			c(types.HardLiquidityRewardDenom, 100),
 | 
			
		||||
			c("swp", 200),
 | 
			
		||||
		).Add(claim.Reward...),
 | 
			
		||||
		cs(c("hard", 100), c("swp", 200)).Add(claim.Reward...),
 | 
			
		||||
		syncedClaim.Reward,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
@ -237,10 +234,7 @@ func (suite *SynchronizeDelegatorRewardTests) TestRewardIsIncreasedWhenGlobalFac
 | 
			
		||||
	syncedClaim, _ := suite.keeper.GetDelegatorClaim(suite.ctx, claim.Owner)
 | 
			
		||||
 | 
			
		||||
	suite.Equal(
 | 
			
		||||
		cs(
 | 
			
		||||
			c(types.HardLiquidityRewardDenom, 100),
 | 
			
		||||
			c("swp", 200),
 | 
			
		||||
		).Add(claim.Reward...),
 | 
			
		||||
		cs(c("hard", 100), c("swp", 200)).Add(claim.Reward...),
 | 
			
		||||
		syncedClaim.Reward,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -53,7 +54,7 @@ func (suite *DelegatorRewardsTestSuite) SetupApp() {
 | 
			
		||||
	suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *DelegatorRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder) {
 | 
			
		||||
func (suite *DelegatorRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder testutil.IncentiveGenesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	suite.app.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
@ -128,7 +129,7 @@ func (suite *DelegatorRewardsTestSuite) TestAccumulateDelegatorRewards() {
 | 
			
		||||
				WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
 | 
			
		||||
				WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -226,7 +227,7 @@ func (suite *DelegatorRewardsTestSuite) TestSynchronizeDelegatorReward() {
 | 
			
		||||
				WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
 | 
			
		||||
				WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -351,7 +352,7 @@ func (suite *DelegatorRewardsTestSuite) TestSimulateDelegatorRewardSynchronizati
 | 
			
		||||
				WithSimpleAccount(suite.addrs[0], cs(c("ukava", 1e9))).
 | 
			
		||||
				WithSimpleAccount(sdk.AccAddress(suite.validatorAddrs[0]), cs(c("ukava", 1e9)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleDelegatorRewardPeriod(tc.args.delegation.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -458,7 +459,7 @@ func (suite *DelegatorRewardsTestSuite) TestUnbondingValidatorSyncsClaim() {
 | 
			
		||||
	rewardsPerSecond := cs(c("hard", 122354))
 | 
			
		||||
	bondDenom := "ukava"
 | 
			
		||||
 | 
			
		||||
	incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
	incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(suite.genesisTime).
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -552,7 +553,7 @@ func (suite *DelegatorRewardsTestSuite) TestBondingValidatorSyncsClaim() {
 | 
			
		||||
	rewardsPerSecond := cs(c("hard", 122354))
 | 
			
		||||
	bondDenom := "ukava"
 | 
			
		||||
 | 
			
		||||
	incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
	incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(suite.genesisTime).
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -644,7 +645,7 @@ func (suite *DelegatorRewardsTestSuite) TestSlashingValidatorSyncsClaim() {
 | 
			
		||||
	rewardsPerSecond := cs(c("hard", 122354))
 | 
			
		||||
	bondDenom := "ukava"
 | 
			
		||||
 | 
			
		||||
	incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
	incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(suite.genesisTime).
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
@ -725,7 +726,7 @@ func (suite *DelegatorRewardsTestSuite) TestRedelegationSyncsClaim() {
 | 
			
		||||
	rewardsPerSecond := cs(c("hard", 122354))
 | 
			
		||||
	bondDenom := "ukava"
 | 
			
		||||
 | 
			
		||||
	incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
	incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
		WithGenesisTime(suite.genesisTime).
 | 
			
		||||
		WithSimpleDelegatorRewardPeriod(bondDenom, rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -53,7 +54,7 @@ func (suite *SupplyRewardsTestSuite) SetupApp() {
 | 
			
		||||
	suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *SupplyRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder, hardBuilder HardGenesisBuilder) {
 | 
			
		||||
func (suite *SupplyRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder testutil.IncentiveGenesisBuilder, hardBuilder testutil.HardGenesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	suite.app.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
@ -163,7 +164,7 @@ func (suite *SupplyRewardsTestSuite) TestAccumulateHardSupplyRewards() {
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
			// suite.SetupWithGenState(authBuilder)
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			if tc.args.rewardsPerSecond != nil {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(tc.args.deposit.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
@ -317,7 +318,7 @@ func (suite *SupplyRewardsTestSuite) TestInitializeHardSupplyRewards() {
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			for moneyMarketDenom, rewardsPerSecond := range tc.args.moneyMarketRewardDenoms {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(moneyMarketDenom, rewardsPerSecond)
 | 
			
		||||
			}
 | 
			
		||||
@ -506,7 +507,7 @@ func (suite *SupplyRewardsTestSuite) TestSynchronizeHardSupplyReward() {
 | 
			
		||||
				WithSimpleAccount(suite.addrs[2], cs(c("ukava", 1e9))).
 | 
			
		||||
				WithSimpleAccount(userAddr, cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)))
 | 
			
		||||
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime)
 | 
			
		||||
			if tc.args.rewardsPerSecond != nil {
 | 
			
		||||
				incentBuilder = incentBuilder.WithSimpleSupplyRewardPeriod(tc.args.incentiveSupplyRewardDenom, tc.args.rewardsPerSecond)
 | 
			
		||||
@ -809,7 +810,7 @@ func (suite *SupplyRewardsTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
			
		||||
				userAddr,
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleSupplyRewardPeriod("bnb", tc.args.rewardsPerSecond).
 | 
			
		||||
				WithSimpleSupplyRewardPeriod("ukava", tc.args.rewardsPerSecond).
 | 
			
		||||
@ -893,7 +894,7 @@ func (suite *SupplyRewardsTestSuite) TestSimulateHardSupplyRewardSynchronization
 | 
			
		||||
				userAddr,
 | 
			
		||||
				cs(c("bnb", 1e15), c("ukava", 1e15), c("btcb", 1e15), c("xrp", 1e15), c("zzz", 1e15)),
 | 
			
		||||
			)
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().
 | 
			
		||||
				WithGenesisTime(suite.genesisTime).
 | 
			
		||||
				WithSimpleSupplyRewardPeriod(tc.args.deposit.Denom, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -110,7 +110,7 @@ func (k Keeper) GetSynchronizedSwapClaim(ctx sdk.Context, owner sdk.AccAddress)
 | 
			
		||||
	for _, indexes := range claim.RewardIndexes {
 | 
			
		||||
		poolID := indexes.CollateralType
 | 
			
		||||
 | 
			
		||||
		shares, found := k.swapKeeper.GetDepositorSharesInPool(ctx, owner, poolID)
 | 
			
		||||
		shares, found := k.swapKeeper.GetDepositorSharesAmount(ctx, owner, poolID)
 | 
			
		||||
		if !found {
 | 
			
		||||
			shares = sdk.ZeroInt()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -258,7 +258,7 @@ type fakeSwapKeeper struct {
 | 
			
		||||
func (k fakeSwapKeeper) GetPoolShares(ctx sdk.Context, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	return k.poolShares, true
 | 
			
		||||
}
 | 
			
		||||
func (k fakeSwapKeeper) GetDepositorSharesInPool(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (sdk.Int, bool) {
 | 
			
		||||
func (k fakeSwapKeeper) GetDepositorSharesAmount(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	// This is just to implement the swap keeper interface.
 | 
			
		||||
	return sdk.Int{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import (
 | 
			
		||||
	cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
 | 
			
		||||
	cdptypes "github.com/kava-labs/kava/x/cdp/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/testutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Test suite used for all keeper tests
 | 
			
		||||
@ -47,7 +48,7 @@ func (suite *USDXRewardsTestSuite) SetupApp() {
 | 
			
		||||
	suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: suite.genesisTime})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *USDXRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder IncentiveGenesisBuilder) {
 | 
			
		||||
func (suite *USDXRewardsTestSuite) SetupWithGenState(authBuilder app.AuthGenesisBuilder, incentBuilder testutil.IncentiveGenesisBuilder) {
 | 
			
		||||
	suite.SetupApp()
 | 
			
		||||
 | 
			
		||||
	suite.app.InitializeFromGenesisStatesWithTime(
 | 
			
		||||
@ -105,7 +106,7 @@ func (suite *USDXRewardsTestSuite) TestAccumulateUSDXMintingRewards() {
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(app.NewAuthGenesisBuilder(), incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -169,7 +170,7 @@ func (suite *USDXRewardsTestSuite) TestSynchronizeUSDXMintingReward() {
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(suite.addrs[0], cs(tc.args.initialCollateral))
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBuilder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
@ -256,7 +257,7 @@ func (suite *USDXRewardsTestSuite) TestSimulateUSDXMintingRewardSynchronization(
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			authBuilder := app.NewAuthGenesisBuilder().WithSimpleAccount(suite.addrs[0], cs(tc.args.initialCollateral))
 | 
			
		||||
			incentBuilder := NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
			incentBuilder := testutil.NewIncentiveGenesisBuilder().WithGenesisTime(suite.genesisTime).WithSimpleUSDXRewardPeriod(tc.args.ctype, tc.args.rewardsPerSecond)
 | 
			
		||||
 | 
			
		||||
			suite.SetupWithGenState(authBuilder, incentBuilder)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								x/incentive/testutil/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								x/incentive/testutil/builder.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
package testutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	hardtypes "github.com/kava-labs/kava/x/hard/types"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	oneYear time.Duration = time.Hour * 24 * 365
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IncentiveGenesisBuilder is a tool for creating an incentive genesis state.
 | 
			
		||||
// Helper methods add values onto a default genesis state.
 | 
			
		||||
// All methods are immutable and return updated copies of the builder.
 | 
			
		||||
type IncentiveGenesisBuilder struct {
 | 
			
		||||
	types.GenesisState
 | 
			
		||||
	genesisTime time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewIncentiveGenesisBuilder() IncentiveGenesisBuilder {
 | 
			
		||||
	return IncentiveGenesisBuilder{
 | 
			
		||||
		GenesisState: types.DefaultGenesisState(),
 | 
			
		||||
		genesisTime:  time.Time{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) Build() types.GenesisState {
 | 
			
		||||
	return builder.GenesisState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) BuildMarshalled() app.GenesisState {
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		types.ModuleName: types.ModuleCdc.MustMarshalJSON(builder.Build()),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithGenesisTime(time time.Time) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.genesisTime = time
 | 
			
		||||
	builder.Params.ClaimEnd = time.Add(5 * oneYear)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedBorrowRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.HardBorrowRewardPeriods = append(builder.Params.HardBorrowRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.HardBorrowAccumulationTimes = append(builder.HardBorrowAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleBorrowRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedBorrowRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedSupplyRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.HardSupplyRewardPeriods = append(builder.Params.HardSupplyRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.HardSupplyAccumulationTimes = append(builder.HardSupplyAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleSupplyRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedSupplyRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedDelegatorRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.DelegatorRewardPeriods = append(builder.Params.DelegatorRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.DelegatorAccumulationTimes = append(builder.DelegatorAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleDelegatorRewardPeriod(ctype string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedDelegatorRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedSwapRewardPeriod(period types.MultiRewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.SwapRewardPeriods = append(builder.Params.SwapRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.SwapAccumulationTimes = append(builder.SwapAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleSwapRewardPeriod(poolID string, rewardsPerSecond sdk.Coins) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedSwapRewardPeriod(types.NewMultiRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		poolID,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithInitializedUSDXRewardPeriod(period types.RewardPeriod) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.USDXMintingRewardPeriods = append(builder.Params.USDXMintingRewardPeriods, period)
 | 
			
		||||
 | 
			
		||||
	accumulationTimeForPeriod := types.NewGenesisAccumulationTime(period.CollateralType, builder.genesisTime)
 | 
			
		||||
	builder.USDXAccumulationTimes = append(builder.USDXAccumulationTimes, accumulationTimeForPeriod)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithSimpleUSDXRewardPeriod(ctype string, rewardsPerSecond sdk.Coin) IncentiveGenesisBuilder {
 | 
			
		||||
	return builder.WithInitializedUSDXRewardPeriod(types.NewRewardPeriod(
 | 
			
		||||
		true,
 | 
			
		||||
		ctype,
 | 
			
		||||
		builder.genesisTime,
 | 
			
		||||
		builder.genesisTime.Add(4*oneYear),
 | 
			
		||||
		rewardsPerSecond,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (builder IncentiveGenesisBuilder) WithMultipliers(multipliers types.Multipliers) IncentiveGenesisBuilder {
 | 
			
		||||
	builder.Params.ClaimMultipliers = multipliers
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HardGenesisBuilder is a tool for creating a hard genesis state.
 | 
			
		||||
// Helper methods add values onto a default genesis state.
 | 
			
		||||
// All methods are immutable and return updated copies of the builder.
 | 
			
		||||
type HardGenesisBuilder struct {
 | 
			
		||||
	hardtypes.GenesisState
 | 
			
		||||
	genesisTime time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHardGenesisBuilder() HardGenesisBuilder {
 | 
			
		||||
	return HardGenesisBuilder{
 | 
			
		||||
		GenesisState: hardtypes.DefaultGenesisState(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) Build() hardtypes.GenesisState {
 | 
			
		||||
	return builder.GenesisState
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) BuildMarshalled() app.GenesisState {
 | 
			
		||||
	return app.GenesisState{
 | 
			
		||||
		hardtypes.ModuleName: hardtypes.ModuleCdc.MustMarshalJSON(builder.Build()),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithGenesisTime(genTime time.Time) HardGenesisBuilder {
 | 
			
		||||
	builder.genesisTime = genTime
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithInitializedMoneyMarket(market hard.MoneyMarket) HardGenesisBuilder {
 | 
			
		||||
	builder.Params.MoneyMarkets = append(builder.Params.MoneyMarkets, market)
 | 
			
		||||
 | 
			
		||||
	builder.PreviousAccumulationTimes = append(
 | 
			
		||||
		builder.PreviousAccumulationTimes,
 | 
			
		||||
		hardtypes.NewGenesisAccumulationTime(market.Denom, builder.genesisTime, sdk.OneDec(), sdk.OneDec()),
 | 
			
		||||
	)
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func (builder HardGenesisBuilder) WithMinBorrow(minUSDValue sdk.Dec) HardGenesisBuilder {
 | 
			
		||||
	builder.Params.MinimumBorrowUSDValue = minUSDValue
 | 
			
		||||
	return builder
 | 
			
		||||
}
 | 
			
		||||
func NewStandardMoneyMarket(denom string) hardtypes.MoneyMarket {
 | 
			
		||||
	return hardtypes.NewMoneyMarket(
 | 
			
		||||
		denom,
 | 
			
		||||
		hard.NewBorrowLimit(
 | 
			
		||||
			false,
 | 
			
		||||
			sdk.NewDec(1e15),
 | 
			
		||||
			sdk.MustNewDecFromStr("0.6"),
 | 
			
		||||
		),
 | 
			
		||||
		denom+":usd",
 | 
			
		||||
		sdk.NewInt(1e6),
 | 
			
		||||
		hard.NewInterestRateModel(
 | 
			
		||||
			sdk.MustNewDecFromStr("0.05"),
 | 
			
		||||
			sdk.MustNewDecFromStr("2"),
 | 
			
		||||
			sdk.MustNewDecFromStr("0.8"),
 | 
			
		||||
			sdk.MustNewDecFromStr("10"),
 | 
			
		||||
		),
 | 
			
		||||
		sdk.MustNewDecFromStr("0.05"),
 | 
			
		||||
		sdk.ZeroDec(),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										167
									
								
								x/incentive/testutil/integration.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								x/incentive/testutil/integration.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,167 @@
 | 
			
		||||
package testutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/staking"
 | 
			
		||||
	supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
	"github.com/tendermint/tendermint/crypto/ed25519"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/cdp"
 | 
			
		||||
	"github.com/kava-labs/kava/x/hard"
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive"
 | 
			
		||||
	"github.com/kava-labs/kava/x/swap"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IntegrationTester struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
	App app.TestApp
 | 
			
		||||
	Ctx sdk.Context
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) NextBlockAt(blockTime time.Time) {
 | 
			
		||||
	if !suite.Ctx.BlockTime().Before(blockTime) {
 | 
			
		||||
		panic(fmt.Sprintf("new block time %s must be after current %s", blockTime, suite.Ctx.BlockTime()))
 | 
			
		||||
	}
 | 
			
		||||
	blockHeight := suite.Ctx.BlockHeight() + 1
 | 
			
		||||
 | 
			
		||||
	_ = suite.App.EndBlocker(suite.Ctx, abci.RequestEndBlock{})
 | 
			
		||||
 | 
			
		||||
	suite.Ctx = suite.Ctx.WithBlockTime(blockTime).WithBlockHeight(blockHeight)
 | 
			
		||||
 | 
			
		||||
	_ = suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{}) // height and time in RequestBeginBlock are ignored by module begin blockers
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) NextBlockAfter(blockDuration time.Duration) {
 | 
			
		||||
	suite.NextBlockAt(suite.Ctx.BlockTime().Add(blockDuration))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverIncentiveMsg(msg sdk.Msg) error {
 | 
			
		||||
	handler := incentive.NewHandler(suite.App.GetIncentiveKeeper())
 | 
			
		||||
	_, err := handler(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverMsgCreateValidator(address sdk.ValAddress, selfDelegation sdk.Coin) error {
 | 
			
		||||
	msg := staking.NewMsgCreateValidator(
 | 
			
		||||
		address,
 | 
			
		||||
		ed25519.GenPrivKey().PubKey(),
 | 
			
		||||
		selfDelegation,
 | 
			
		||||
		staking.Description{},
 | 
			
		||||
		staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
 | 
			
		||||
		sdk.NewInt(1_000_000),
 | 
			
		||||
	)
 | 
			
		||||
	handler := staking.NewHandler(suite.App.GetStakingKeeper())
 | 
			
		||||
	_, err := handler(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverMsgDelegate(delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error {
 | 
			
		||||
	msg := staking.NewMsgDelegate(
 | 
			
		||||
		delegator,
 | 
			
		||||
		validator,
 | 
			
		||||
		amount,
 | 
			
		||||
	)
 | 
			
		||||
	handleStakingMsg := staking.NewHandler(suite.App.GetStakingKeeper())
 | 
			
		||||
	_, err := handleStakingMsg(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverSwapMsgDeposit(depositor sdk.AccAddress, tokenA, tokenB sdk.Coin, slippage sdk.Dec) error {
 | 
			
		||||
	msg := swap.NewMsgDeposit(
 | 
			
		||||
		depositor,
 | 
			
		||||
		tokenA,
 | 
			
		||||
		tokenB,
 | 
			
		||||
		slippage,
 | 
			
		||||
		suite.Ctx.BlockTime().Add(time.Hour).Unix(), // ensure msg will not fail due to short deadline
 | 
			
		||||
	)
 | 
			
		||||
	_, err := swap.NewHandler(suite.App.GetSwapKeeper())(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverHardMsgDeposit(depositor sdk.AccAddress, deposit sdk.Coins) error {
 | 
			
		||||
	msg := hard.NewMsgDeposit(depositor, deposit)
 | 
			
		||||
	_, err := hard.NewHandler(suite.App.GetHardKeeper())(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverHardMsgBorrow(depositor sdk.AccAddress, borrow sdk.Coins) error {
 | 
			
		||||
	msg := hard.NewMsgBorrow(depositor, borrow)
 | 
			
		||||
	_, err := hard.NewHandler(suite.App.GetHardKeeper())(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DeliverMsgCreateCDP(owner sdk.AccAddress, collateral, principal sdk.Coin, collateralType string) error {
 | 
			
		||||
	msg := cdp.NewMsgCreateCDP(owner, collateral, principal, collateralType)
 | 
			
		||||
	_, err := cdp.NewHandler(suite.App.GetCDPKeeper())(suite.Ctx, msg)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) GetAccount(addr sdk.AccAddress) authexported.Account {
 | 
			
		||||
	ak := suite.App.GetAccountKeeper()
 | 
			
		||||
	return ak.GetAccount(suite.Ctx, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) GetModuleAccount(name string) supplyexported.ModuleAccountI {
 | 
			
		||||
	sk := suite.App.GetSupplyKeeper()
 | 
			
		||||
	return sk.GetModuleAccount(suite.Ctx, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) GetBalance(address sdk.AccAddress) sdk.Coins {
 | 
			
		||||
	acc := suite.App.GetAccountKeeper().GetAccount(suite.Ctx, address)
 | 
			
		||||
	if acc != nil {
 | 
			
		||||
		return acc.GetCoins()
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) ErrorIs(err, target error) bool {
 | 
			
		||||
	return suite.Truef(errors.Is(err, target), "err didn't match: %s, it was: %s", target, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) BalanceEquals(address sdk.AccAddress, expected sdk.Coins) {
 | 
			
		||||
	acc := suite.App.GetAccountKeeper().GetAccount(suite.Ctx, address)
 | 
			
		||||
	suite.Require().NotNil(acc, "expected account to not be nil")
 | 
			
		||||
	suite.Equalf(expected, acc.GetCoins(), "expected account balance to equal coins %s, but got %s", expected, acc.GetCoins())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) VestingPeriodsEqual(address sdk.AccAddress, expectedPeriods vesting.Periods) {
 | 
			
		||||
	acc := suite.App.GetAccountKeeper().GetAccount(suite.Ctx, address)
 | 
			
		||||
	suite.Require().NotNil(acc, "expected vesting account not to be nil")
 | 
			
		||||
	vacc, ok := acc.(*vesting.PeriodicVestingAccount)
 | 
			
		||||
	suite.Require().True(ok, "expected vesting account to be type PeriodicVestingAccount")
 | 
			
		||||
	suite.Equal(expectedPeriods, vacc.VestingPeriods)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) SwapRewardEquals(owner sdk.AccAddress, expected sdk.Coins) {
 | 
			
		||||
	claim, found := suite.App.GetIncentiveKeeper().GetSwapClaim(suite.Ctx, owner)
 | 
			
		||||
	suite.Require().Truef(found, "expected swap claim to be found for %s", owner)
 | 
			
		||||
	suite.Equalf(expected, claim.Reward, "expected swap claim reward to be %s, but got %s", expected, claim.Reward)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) DelegatorRewardEquals(owner sdk.AccAddress, expected sdk.Coins) {
 | 
			
		||||
	claim, found := suite.App.GetIncentiveKeeper().GetDelegatorClaim(suite.Ctx, owner)
 | 
			
		||||
	suite.Require().Truef(found, "expected delegator claim to be found for %s", owner)
 | 
			
		||||
	suite.Equalf(expected, claim.Reward, "expected delegator claim reward to be %s, but got %s", expected, claim.Reward)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) HardRewardEquals(owner sdk.AccAddress, expected sdk.Coins) {
 | 
			
		||||
	claim, found := suite.App.GetIncentiveKeeper().GetHardLiquidityProviderClaim(suite.Ctx, owner)
 | 
			
		||||
	suite.Require().Truef(found, "expected delegator claim to be found for %s", owner)
 | 
			
		||||
	suite.Equalf(expected, claim.Reward, "expected delegator claim reward to be %s, but got %s", expected, claim.Reward)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTester) USDXRewardEquals(owner sdk.AccAddress, expected sdk.Coin) {
 | 
			
		||||
	claim, found := suite.App.GetIncentiveKeeper().GetUSDXMintingClaim(suite.Ctx, owner)
 | 
			
		||||
	suite.Require().Truef(found, "expected delegator claim to be found for %s", owner)
 | 
			
		||||
	suite.Equalf(expected, claim.Reward, "expected delegator claim reward to be %s, but got %s", expected, claim.Reward)
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetTotalVestingPeriodLength returns the summed length of all vesting periods
 | 
			
		||||
func GetTotalVestingPeriodLength(periods vesting.Periods) int64 {
 | 
			
		||||
	length := int64(0)
 | 
			
		||||
	for _, period := range periods {
 | 
			
		||||
		length += period.Length
 | 
			
		||||
	}
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
@ -1,53 +0,0 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type accountTest struct {
 | 
			
		||||
	periods     vesting.Periods
 | 
			
		||||
	expectedVal int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	tests []accountTest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *AccountTestSuite) SetupTest() {
 | 
			
		||||
	tests := []accountTest{
 | 
			
		||||
		{
 | 
			
		||||
			periods: vesting.Periods{
 | 
			
		||||
				vesting.Period{
 | 
			
		||||
					Length: int64(100),
 | 
			
		||||
					Amount: sdk.Coins{},
 | 
			
		||||
				},
 | 
			
		||||
				vesting.Period{
 | 
			
		||||
					Length: int64(200),
 | 
			
		||||
					Amount: sdk.Coins{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedVal: int64(300),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.tests = tests
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *AccountTestSuite) TestGetTotalPeriodLength() {
 | 
			
		||||
	for _, t := range suite.tests {
 | 
			
		||||
		length := types.GetTotalVestingPeriodLength(t.periods)
 | 
			
		||||
		suite.Equal(t.expectedVal, length)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAccountTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(AccountTestSuite))
 | 
			
		||||
}
 | 
			
		||||
@ -13,7 +13,8 @@ const (
 | 
			
		||||
	HardLiquidityProviderClaimType = "hard_liquidity_provider"
 | 
			
		||||
	DelegatorClaimType             = "delegator_claim"
 | 
			
		||||
	SwapClaimType                  = "swap"
 | 
			
		||||
	BondDenom                      = "ukava"
 | 
			
		||||
 | 
			
		||||
	BondDenom = "ukava"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Claim is an interface for handling common claim actions
 | 
			
		||||
 | 
			
		||||
@ -18,12 +18,16 @@ func RegisterCodec(cdc *codec.Codec) {
 | 
			
		||||
	cdc.RegisterConcrete(USDXMintingClaim{}, "incentive/USDXMintingClaim", nil)
 | 
			
		||||
	cdc.RegisterConcrete(HardLiquidityProviderClaim{}, "incentive/HardLiquidityProviderClaim", nil)
 | 
			
		||||
	cdc.RegisterConcrete(DelegatorClaim{}, "incentive/DelegatorClaim", nil)
 | 
			
		||||
	cdc.RegisterConcrete(SwapClaim{}, "incentive/SwapClaim", nil)
 | 
			
		||||
 | 
			
		||||
	// Register msgs
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimUSDXMintingReward{}, "incentive/MsgClaimUSDXMintingReward", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimHardReward{}, "incentive/MsgClaimHardReward", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimDelegatorReward{}, "incentive/MsgClaimDelegatorReward", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimSwapReward{}, "incentive/MsgClaimSwapReward", nil)
 | 
			
		||||
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimUSDXMintingRewardVVesting{}, "incentive/MsgClaimUSDXRewardVVesting", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimHardRewardVVesting{}, "incentive/MsgClaimHardRewardVVesting", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimDelegatorRewardVVesting{}, "incentive/MsgClaimDelegatorRewardVVesting", nil)
 | 
			
		||||
	cdc.RegisterConcrete(MsgClaimSwapRewardVVesting{}, "incentive/MsgClaimSwapRewardVVesting", nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,4 +20,5 @@ var (
 | 
			
		||||
	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")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,7 @@ type HardKeeper interface {
 | 
			
		||||
// SwapKeeper defines the required methods needed by this modules keeper
 | 
			
		||||
type SwapKeeper interface {
 | 
			
		||||
	GetPoolShares(ctx sdk.Context, poolID string) (shares sdk.Int, found bool)
 | 
			
		||||
	GetDepositorSharesInPool(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (shares sdk.Int, found bool)
 | 
			
		||||
	GetDepositorSharesAmount(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (shares sdk.Int, found bool)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AccountKeeper defines the expected keeper interface for interacting with account
 | 
			
		||||
 | 
			
		||||
@ -17,10 +17,6 @@ const (
 | 
			
		||||
	QuerierRoute = ModuleName
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TODO: Refactor so that each incentive type has:
 | 
			
		||||
// 1. [Incentive]ClaimKeyPrefix
 | 
			
		||||
// 2. [Incentve]AccumulatorKeyPrefix { PreviousAccrualTime block.Time, IndexFactors types.IndexFactors }
 | 
			
		||||
 | 
			
		||||
// Key Prefixes
 | 
			
		||||
var (
 | 
			
		||||
	USDXMintingClaimKeyPrefix                     = []byte{0x01} // prefix for keys that store USDX minting claims
 | 
			
		||||
@ -38,6 +34,5 @@ var (
 | 
			
		||||
	SwapRewardIndexesKeyPrefix                    = []byte{0x13} // prefix for key that stores swap reward indexes
 | 
			
		||||
	PreviousSwapRewardAccrualTimeKeyPrefix        = []byte{0x14} // prefix for key that stores the previous time swap rewards accrued
 | 
			
		||||
 | 
			
		||||
	USDXMintingRewardDenom   = "ukava"
 | 
			
		||||
	HardLiquidityRewardDenom = "hard"
 | 
			
		||||
	USDXMintingRewardDenom = "ukava"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,21 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const MaxDenomsToClaim = 1000
 | 
			
		||||
 | 
			
		||||
// ensure Msg interface compliance at compile time
 | 
			
		||||
var _ sdk.Msg = &MsgClaimUSDXMintingReward{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimUSDXMintingRewardVVesting{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimHardReward{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimHardRewardVVesting{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimUSDXMintingRewardVVesting{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimDelegatorReward{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimDelegatorRewardVVesting{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimSwapReward{}
 | 
			
		||||
var _ sdk.Msg = &MsgClaimSwapRewardVVesting{}
 | 
			
		||||
 | 
			
		||||
// MsgClaimUSDXMintingReward message type used to claim USDX minting rewards
 | 
			
		||||
type MsgClaimUSDXMintingReward struct {
 | 
			
		||||
@ -38,7 +42,10 @@ func (msg MsgClaimUSDXMintingReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -84,7 +91,10 @@ func (msg MsgClaimUSDXMintingRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -102,13 +112,15 @@ func (msg MsgClaimUSDXMintingRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimHardReward returns a new MsgClaimHardReward.
 | 
			
		||||
func NewMsgClaimHardReward(sender sdk.AccAddress, multiplierName string) MsgClaimHardReward {
 | 
			
		||||
func NewMsgClaimHardReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimHardReward {
 | 
			
		||||
	return MsgClaimHardReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -125,7 +137,18 @@ func (msg MsgClaimHardReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -144,14 +167,16 @@ 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimHardRewardVVesting returns a new MsgClaimHardRewardVVesting.
 | 
			
		||||
func NewMsgClaimHardRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string) MsgClaimHardRewardVVesting {
 | 
			
		||||
func NewMsgClaimHardRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimHardRewardVVesting {
 | 
			
		||||
	return MsgClaimHardRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -171,7 +196,18 @@ func (msg MsgClaimHardRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -189,13 +225,15 @@ func (msg MsgClaimHardRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimDelegatorReward returns a new MsgClaimDelegatorReward.
 | 
			
		||||
func NewMsgClaimDelegatorReward(sender sdk.AccAddress, multiplierName string) MsgClaimDelegatorReward {
 | 
			
		||||
func NewMsgClaimDelegatorReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimDelegatorReward {
 | 
			
		||||
	return MsgClaimDelegatorReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -212,7 +250,18 @@ func (msg MsgClaimDelegatorReward) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -231,14 +280,16 @@ 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgClaimDelegatorRewardVVesting returns a new MsgClaimDelegatorRewardVVesting.
 | 
			
		||||
func NewMsgClaimDelegatorRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string) MsgClaimDelegatorRewardVVesting {
 | 
			
		||||
func NewMsgClaimDelegatorRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimDelegatorRewardVVesting {
 | 
			
		||||
	return MsgClaimDelegatorRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -258,7 +309,18 @@ func (msg MsgClaimDelegatorRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
@ -271,3 +333,116 @@ func (msg MsgClaimDelegatorRewardVVesting) GetSignBytes() []byte {
 | 
			
		||||
func (msg MsgClaimDelegatorRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
	return []sdk.AccAddress{msg.Sender}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgClaimSwapReward message type used to claim delegator rewards
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMsgClaimSwapReward returns a new MsgClaimSwapReward.
 | 
			
		||||
func NewMsgClaimSwapReward(sender sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimSwapReward {
 | 
			
		||||
	return MsgClaimSwapReward{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Route return the message type used for routing the message.
 | 
			
		||||
func (msg MsgClaimSwapReward) Route() string { return RouterKey }
 | 
			
		||||
 | 
			
		||||
// Type returns a human-readable string for the message, intended for utilization within tags.
 | 
			
		||||
func (msg MsgClaimSwapReward) Type() string {
 | 
			
		||||
	return "claim_swap_reward"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateBasic does a simple validation check that doesn't require access to state.
 | 
			
		||||
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 {
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
func (msg MsgClaimSwapReward) GetSignBytes() []byte {
 | 
			
		||||
	bz := ModuleCdc.MustMarshalJSON(msg)
 | 
			
		||||
	return sdk.MustSortJSON(bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSigners returns the addresses of signers that must sign.
 | 
			
		||||
func (msg MsgClaimSwapReward) GetSigners() []sdk.AccAddress {
 | 
			
		||||
	return []sdk.AccAddress{msg.Sender}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgClaimSwapRewardVVesting returns a new MsgClaimSwapRewardVVesting.
 | 
			
		||||
func NewMsgClaimSwapRewardVVesting(sender, receiver sdk.AccAddress, multiplierName string, denomsToClaim []string) MsgClaimSwapRewardVVesting {
 | 
			
		||||
	return MsgClaimSwapRewardVVesting{
 | 
			
		||||
		Sender:         sender,
 | 
			
		||||
		Receiver:       receiver,
 | 
			
		||||
		MultiplierName: multiplierName,
 | 
			
		||||
		DenomsToClaim:  denomsToClaim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Route return the message type used for routing the message.
 | 
			
		||||
func (msg MsgClaimSwapRewardVVesting) Route() string { return RouterKey }
 | 
			
		||||
 | 
			
		||||
// Type returns a human-readable string for the message, intended for utilization within tags.
 | 
			
		||||
func (msg MsgClaimSwapRewardVVesting) Type() string {
 | 
			
		||||
	return "claim_swap_reward_vvesting"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateBasic does a simple validation check that doesn't require access to state.
 | 
			
		||||
func (msg MsgClaimSwapRewardVVesting) ValidateBasic() error {
 | 
			
		||||
	if msg.Sender.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if msg.Receiver.Empty() {
 | 
			
		||||
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if err := MultiplierName(msg.MultiplierName).IsValid(); 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSignBytes gets the canonical byte representation of the Msg.
 | 
			
		||||
func (msg MsgClaimSwapRewardVVesting) GetSignBytes() []byte {
 | 
			
		||||
	bz := ModuleCdc.MustMarshalJSON(msg)
 | 
			
		||||
	return sdk.MustSortJSON(bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSigners returns the addresses of signers that must sign.
 | 
			
		||||
func (msg MsgClaimSwapRewardVVesting) GetSigners() []sdk.AccAddress {
 | 
			
		||||
	return []sdk.AccAddress{msg.Sender}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,72 +1,522 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"github.com/tendermint/tendermint/crypto"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type msgTest struct {
 | 
			
		||||
	from           sdk.AccAddress
 | 
			
		||||
	multiplierName string
 | 
			
		||||
	expectPass     bool
 | 
			
		||||
}
 | 
			
		||||
func TestMsgClaimVVesting_Validate(t *testing.T) {
 | 
			
		||||
	validAddress := sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1")))
 | 
			
		||||
 | 
			
		||||
type MsgTestSuite struct {
 | 
			
		||||
	suite.Suite
 | 
			
		||||
 | 
			
		||||
	tests []msgTest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *MsgTestSuite) SetupTest() {
 | 
			
		||||
	tests := []msgTest{
 | 
			
		||||
	type expectedErr struct {
 | 
			
		||||
		wraps error
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		receiver       sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
		denomsToClaim  []string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		msgArgs msgArgs
 | 
			
		||||
		expect  expectedErr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			from:           sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			multiplierName: "large",
 | 
			
		||||
			expectPass:     true,
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			from:           sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			multiplierName: "medium",
 | 
			
		||||
			expectPass:     true,
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			from:           sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			multiplierName: "small",
 | 
			
		||||
			expectPass:     true,
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			from:           sdk.AccAddress{},
 | 
			
		||||
			multiplierName: "medium",
 | 
			
		||||
			expectPass:     false,
 | 
			
		||||
			name: "empty denoms to claim is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			from:           sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1"))),
 | 
			
		||||
			multiplierName: "huge",
 | 
			
		||||
			expectPass:     false,
 | 
			
		||||
			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",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			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"},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "too many claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  tooManyClaimDenoms(),
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	suite.tests = tests
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *MsgTestSuite) TestMsgValidation() {
 | 
			
		||||
	for _, t := range suite.tests {
 | 
			
		||||
		msg := types.NewMsgClaimUSDXMintingReward(t.from, t.multiplierName)
 | 
			
		||||
		err := msg.ValidateBasic()
 | 
			
		||||
		if t.expectPass {
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
		} else {
 | 
			
		||||
			suite.Require().Error(err)
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		msgs := []sdk.Msg{
 | 
			
		||||
			types.NewMsgClaimHardRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimDelegatorRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
			types.NewMsgClaimSwapRewardVVesting(
 | 
			
		||||
				tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName, tc.msgArgs.denomsToClaim,
 | 
			
		||||
			),
 | 
			
		||||
		}
 | 
			
		||||
		for _, msg := range msgs {
 | 
			
		||||
			t.Run(msg.Type()+" "+tc.name, func(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
				err := msg.ValidateBasic()
 | 
			
		||||
				if tc.expect.pass {
 | 
			
		||||
					require.NoError(t, err)
 | 
			
		||||
				} else {
 | 
			
		||||
					require.Truef(t, errors.Is(err, tc.expect.wraps), "expected error '%s' was not actual '%s'", tc.expect.wraps, err)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMsgTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(MsgTestSuite))
 | 
			
		||||
func TestMsgClaim_Validate(t *testing.T) {
 | 
			
		||||
	validAddress := sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1")))
 | 
			
		||||
 | 
			
		||||
	type expectedErr struct {
 | 
			
		||||
		wraps error
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
		denomsToClaim  []string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		msgArgs msgArgs
 | 
			
		||||
		expect  expectedErr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				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",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "invalid claim denom",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  []string{"a denom string that is invalid because it is much too long"},
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "too many claim denoms",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
				denomsToClaim:  tooManyClaimDenoms(),
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidClaimDenoms,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
			),
 | 
			
		||||
		}
 | 
			
		||||
		for _, msg := range msgs {
 | 
			
		||||
			t.Run(msg.Type()+" "+tc.name, func(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
				err := msg.ValidateBasic()
 | 
			
		||||
				if tc.expect.pass {
 | 
			
		||||
					require.NoError(t, err)
 | 
			
		||||
				} else {
 | 
			
		||||
					require.Truef(t, errors.Is(err, tc.expect.wraps), "expected error '%s' was not actual '%s'", tc.expect.wraps, err)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMsgClaimUSDXMintingRewardVVesting_Validate(t *testing.T) {
 | 
			
		||||
	validAddress := sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1")))
 | 
			
		||||
 | 
			
		||||
	type expectedErr struct {
 | 
			
		||||
		wraps error
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		receiver       sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		msgArgs msgArgs
 | 
			
		||||
		expect  expectedErr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
			},
 | 
			
		||||
			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",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				receiver:       validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
			msg := types.NewMsgClaimUSDXMintingRewardVVesting(tc.msgArgs.sender, tc.msgArgs.receiver, tc.msgArgs.multiplierName)
 | 
			
		||||
 | 
			
		||||
			err := msg.ValidateBasic()
 | 
			
		||||
			if tc.expect.pass {
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
			} else {
 | 
			
		||||
				require.Truef(t, errors.Is(err, tc.expect.wraps), "expected error '%s' was not actual '%s'", tc.expect.wraps, err)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMsgClaimUSDXMintingReward_Validate(t *testing.T) {
 | 
			
		||||
	validAddress := sdk.AccAddress(crypto.AddressHash([]byte("KavaTest1")))
 | 
			
		||||
 | 
			
		||||
	type expectedErr struct {
 | 
			
		||||
		wraps error
 | 
			
		||||
		pass  bool
 | 
			
		||||
	}
 | 
			
		||||
	type msgArgs struct {
 | 
			
		||||
		sender         sdk.AccAddress
 | 
			
		||||
		multiplierName string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		msgArgs msgArgs
 | 
			
		||||
		expect  expectedErr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "large multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "medium multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "medium",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				pass: true,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "small multiplier is valid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "small",
 | 
			
		||||
			},
 | 
			
		||||
			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",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multiplier with capitalization is invalid",
 | 
			
		||||
			msgArgs: msgArgs{
 | 
			
		||||
				sender:         validAddress,
 | 
			
		||||
				multiplierName: "Large",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expectedErr{
 | 
			
		||||
				wraps: types.ErrInvalidMultiplier,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			msg := types.NewMsgClaimUSDXMintingReward(tc.msgArgs.sender, tc.msgArgs.multiplierName)
 | 
			
		||||
 | 
			
		||||
			err := msg.ValidateBasic()
 | 
			
		||||
			if tc.expect.pass {
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
			} else {
 | 
			
		||||
				require.Truef(t, errors.Is(err, tc.expect.wraps), "expected error '%s' was not actual '%s'", tc.expect.wraps, err)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tooManyClaimDenoms() []string {
 | 
			
		||||
	claimDenoms := make([]string, types.MaxDenomsToClaim+1)
 | 
			
		||||
	for i := range claimDenoms {
 | 
			
		||||
		claimDenoms[i] = fmt.Sprintf("denom%d", i)
 | 
			
		||||
	}
 | 
			
		||||
	return claimDenoms
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ 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"
 | 
			
		||||
@ -52,7 +53,7 @@ type Params struct {
 | 
			
		||||
	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" json:"swap_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"`
 | 
			
		||||
}
 | 
			
		||||
@ -418,5 +419,5 @@ func (mn MultiplierName) IsValid() error {
 | 
			
		||||
	case Small, Medium, Large:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("invalid multiplier name: %s", mn)
 | 
			
		||||
	return sdkerrors.Wrapf(ErrInvalidMultiplier, "invalid multiplier name: %s", mn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										41
									
								
								x/incentive/types/sdk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								x/incentive/types/sdk.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetTotalVestingPeriodLength returns the summed length of all vesting periods
 | 
			
		||||
func GetTotalVestingPeriodLength(periods vesting.Periods) int64 {
 | 
			
		||||
	length := int64(0)
 | 
			
		||||
	for _, period := range periods {
 | 
			
		||||
		length += period.Length
 | 
			
		||||
	}
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										170
									
								
								x/incentive/types/sdk_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								x/incentive/types/sdk_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,170 @@
 | 
			
		||||
package types_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestGetTotalVestingPeriodLength(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		periods     vesting.Periods
 | 
			
		||||
		expectedVal int64
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "two period lengths are added together",
 | 
			
		||||
			periods: vesting.Periods{
 | 
			
		||||
				{
 | 
			
		||||
					Length: 100,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Length: 200,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expectedVal: 300,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "no periods returns zero",
 | 
			
		||||
			periods:     nil,
 | 
			
		||||
			expectedVal: 0,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			length := types.GetTotalVestingPeriodLength(tc.periods)
 | 
			
		||||
			require.Equal(t, tc.expectedVal, length)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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),
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -5,6 +5,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/swap/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -118,6 +119,8 @@ func (k Keeper) initializePool(ctx sdk.Context, poolID string, depositor sdk.Acc
 | 
			
		||||
	k.SetPool(ctx, poolRecord)
 | 
			
		||||
	k.SetDepositorShares(ctx, shareRecord)
 | 
			
		||||
 | 
			
		||||
	k.hooks.AfterPoolDepositCreated(ctx, poolRecord.PoolID, depositor, shareRecord.SharesOwned)
 | 
			
		||||
 | 
			
		||||
	return pool.Reserves(), pool.TotalShares(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -131,8 +134,10 @@ func (k Keeper) addLiquidityToPool(ctx sdk.Context, record types.PoolRecord, dep
 | 
			
		||||
 | 
			
		||||
	poolRecord := types.NewPoolRecord(pool)
 | 
			
		||||
 | 
			
		||||
	shareRecord, found := k.GetDepositorShares(ctx, depositor, poolRecord.PoolID)
 | 
			
		||||
	if found {
 | 
			
		||||
	shareRecord, sharesFound := k.GetDepositorShares(ctx, depositor, poolRecord.PoolID)
 | 
			
		||||
	if sharesFound {
 | 
			
		||||
		k.hooks.BeforePoolDepositModified(ctx, poolRecord.PoolID, depositor, shareRecord.SharesOwned)
 | 
			
		||||
 | 
			
		||||
		shareRecord.SharesOwned = shareRecord.SharesOwned.Add(shares)
 | 
			
		||||
	} else {
 | 
			
		||||
		shareRecord = types.NewShareRecord(depositor, poolRecord.PoolID, shares)
 | 
			
		||||
@ -141,5 +146,9 @@ func (k Keeper) addLiquidityToPool(ctx sdk.Context, record types.PoolRecord, dep
 | 
			
		||||
	k.SetPool(ctx, poolRecord)
 | 
			
		||||
	k.SetDepositorShares(ctx, shareRecord)
 | 
			
		||||
 | 
			
		||||
	if !sharesFound {
 | 
			
		||||
		k.hooks.AfterPoolDepositCreated(ctx, poolRecord.PoolID, depositor, shareRecord.SharesOwned)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return depositAmount, shares, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,21 +6,9 @@ import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/swap/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (k Keeper) GetPoolShares(ctx sdk.Context, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	// FIXME return pool shares once merged with acceptance branch
 | 
			
		||||
	return sdk.Int{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (k *Keeper) GetDepositorSharesInPool(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	// FIXME return depositor shares once merged with acceptance branch
 | 
			
		||||
	return sdk.Int{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements SwapHooks interface
 | 
			
		||||
var _ types.SwapHooks = Keeper{}
 | 
			
		||||
 | 
			
		||||
// FIXME call hooks within pool logic
 | 
			
		||||
 | 
			
		||||
// AfterPoolDepositCreated - call hook if registered
 | 
			
		||||
func (k Keeper) AfterPoolDepositCreated(ctx sdk.Context, poolID string, depositor sdk.AccAddress, sharesOwned sdk.Int) {
 | 
			
		||||
	if k.hooks != nil {
 | 
			
		||||
 | 
			
		||||
@ -117,6 +117,15 @@ func (k Keeper) GetAllPools(ctx sdk.Context) (records types.PoolRecords) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPoolShares gets the total shares in a pool from the store
 | 
			
		||||
func (k Keeper) GetPoolShares(ctx sdk.Context, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	pool, found := k.GetPool(ctx, poolID)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdk.Int{}, false
 | 
			
		||||
	}
 | 
			
		||||
	return pool.TotalShares, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDepositorShares gets a share record from the store
 | 
			
		||||
func (k Keeper) GetDepositorShares(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (types.ShareRecord, bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.DepositorPoolSharesPrefix)
 | 
			
		||||
@ -187,3 +196,12 @@ func (k Keeper) GetAllDepositorSharesByOwner(ctx sdk.Context, owner sdk.AccAddre
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDepositorSharesAmount gets a depositor's shares in a pool from the store
 | 
			
		||||
func (k *Keeper) GetDepositorSharesAmount(ctx sdk.Context, depositor sdk.AccAddress, poolID string) (sdk.Int, bool) {
 | 
			
		||||
	record, found := k.GetDepositorShares(ctx, depositor, poolID)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdk.Int{}, false
 | 
			
		||||
	}
 | 
			
		||||
	return record.SharesOwned, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -96,6 +96,10 @@ func (suite *keeperTestSuite) TestPool_Persistance() {
 | 
			
		||||
	suite.True(ok)
 | 
			
		||||
	suite.Equal(record, savedRecord)
 | 
			
		||||
 | 
			
		||||
	savedShares, ok := suite.Keeper.GetPoolShares(suite.Ctx, record.PoolID)
 | 
			
		||||
	suite.True(ok)
 | 
			
		||||
	suite.Equal(record.TotalShares, savedShares)
 | 
			
		||||
 | 
			
		||||
	suite.Keeper.DeletePool(suite.Ctx, record.PoolID)
 | 
			
		||||
	deletedPool, ok := suite.Keeper.GetPool(suite.Ctx, record.PoolID)
 | 
			
		||||
	suite.False(ok)
 | 
			
		||||
@ -114,6 +118,10 @@ func (suite *keeperTestSuite) TestShare_Persistance() {
 | 
			
		||||
	suite.True(ok)
 | 
			
		||||
	suite.Equal(record, savedRecord)
 | 
			
		||||
 | 
			
		||||
	savedShares, ok := suite.Keeper.GetDepositorSharesAmount(suite.Ctx, depositor, poolID)
 | 
			
		||||
	suite.True(ok)
 | 
			
		||||
	suite.Equal(record.SharesOwned, savedShares)
 | 
			
		||||
 | 
			
		||||
	suite.Keeper.DeleteDepositorShares(suite.Ctx, depositor, poolID)
 | 
			
		||||
	deletedShares, ok := suite.Keeper.GetDepositorShares(suite.Ctx, depositor, poolID)
 | 
			
		||||
	suite.False(ok)
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,8 @@ func (k Keeper) Withdraw(ctx sdk.Context, owner sdk.AccAddress, shares sdk.Int,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.updatePool(ctx, poolID, pool)
 | 
			
		||||
 | 
			
		||||
	k.hooks.BeforePoolDepositModified(ctx, poolID, owner, shareRecord.SharesOwned)
 | 
			
		||||
	k.updateShares(ctx, owner, poolID, shareRecord.SharesOwned.Sub(shares))
 | 
			
		||||
 | 
			
		||||
	err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleAccountName, owner, withdrawnAmount)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user