Incentive PR 4: claim Hard rewards via the Incentive module (#780)

* claim hard reward keeper methods

* test hard claim payout

* claim hard rewards via cli

* query hard claims via cli

* rest txs and queries

* add handler test

* add claim type event field
This commit is contained in:
Denali Marsh 2021-01-26 12:52:34 +01:00 committed by GitHub
parent 72a6df17fd
commit 3a08fc582b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 610 additions and 72 deletions

View File

@ -23,7 +23,8 @@ const (
ModuleName = types.ModuleName ModuleName = types.ModuleName
QuerierRoute = types.QuerierRoute QuerierRoute = types.QuerierRoute
QueryGetClaimPeriods = types.QueryGetClaimPeriods QueryGetClaimPeriods = types.QueryGetClaimPeriods
QueryGetClaims = types.QueryGetClaims QueryGetCdpClaims = types.QueryGetCdpClaims
QueryGetHardClaims = types.QueryGetHardClaims
QueryGetParams = types.QueryGetParams QueryGetParams = types.QueryGetParams
QueryGetRewardPeriods = types.QueryGetRewardPeriods QueryGetRewardPeriods = types.QueryGetRewardPeriods
RestClaimCollateralType = types.RestClaimCollateralType RestClaimCollateralType = types.RestClaimCollateralType
@ -35,24 +36,27 @@ const (
var ( var (
// function aliases // function aliases
CalculateTimeElapsed = keeper.CalculateTimeElapsed CalculateTimeElapsed = keeper.CalculateTimeElapsed
NewKeeper = keeper.NewKeeper NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier NewQuerier = keeper.NewQuerier
DefaultGenesisState = types.DefaultGenesisState DefaultGenesisState = types.DefaultGenesisState
DefaultParams = types.DefaultParams DefaultParams = types.DefaultParams
GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength
NewGenesisAccumulationTime = types.NewGenesisAccumulationTime NewGenesisAccumulationTime = types.NewGenesisAccumulationTime
NewGenesisState = types.NewGenesisState NewGenesisState = types.NewGenesisState
NewMsgClaimUSDXMintingReward = types.NewMsgClaimUSDXMintingReward NewMsgClaimUSDXMintingReward = types.NewMsgClaimUSDXMintingReward
NewMultiplier = types.NewMultiplier NewMsgClaimHardLiquidityProviderReward = types.NewMsgClaimHardLiquidityProviderReward
NewParams = types.NewParams NewMultiplier = types.NewMultiplier
NewPeriod = types.NewPeriod NewParams = types.NewParams
NewQueryClaimsParams = types.NewQueryClaimsParams NewPeriod = types.NewPeriod
NewRewardIndex = types.NewRewardIndex NewQueryCdpClaimsParams = types.NewQueryCdpClaimsParams
NewRewardPeriod = types.NewRewardPeriod NewQueryHardClaimsParams = types.NewQueryHardClaimsParams
NewUSDXMintingClaim = types.NewUSDXMintingClaim NewRewardIndex = types.NewRewardIndex
ParamKeyTable = types.ParamKeyTable NewRewardPeriod = types.NewRewardPeriod
RegisterCodec = types.RegisterCodec NewUSDXMintingClaim = types.NewUSDXMintingClaim
NewHardLiquidityProviderClaim = types.NewHardLiquidityProviderClaim
ParamKeyTable = types.ParamKeyTable
RegisterCodec = types.RegisterCodec
// variable aliases // variable aliases
PreviousUSDXMintingRewardAccrualTimeKeyPrefix = types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix PreviousUSDXMintingRewardAccrualTimeKeyPrefix = types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix
@ -99,7 +103,8 @@ type (
Multipliers = types.Multipliers Multipliers = types.Multipliers
Params = types.Params Params = types.Params
PostClaimReq = types.PostClaimReq PostClaimReq = types.PostClaimReq
QueryClaimsParams = types.QueryClaimsParams QueryCdpClaimsParams = types.QueryCdpClaimsParams
QueryHardClaimsParams = types.QueryHardClaimsParams
RewardIndex = types.RewardIndex RewardIndex = types.RewardIndex
RewardIndexes = types.RewardIndexes RewardIndexes = types.RewardIndexes
RewardPeriod = types.RewardPeriod RewardPeriod = types.RewardPeriod

View File

@ -25,7 +25,8 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
incentiveQueryCmd.AddCommand(flags.GetCommands( incentiveQueryCmd.AddCommand(flags.GetCommands(
queryParamsCmd(queryRoute, cdc), queryParamsCmd(queryRoute, cdc),
queryClaimsCmd(queryRoute, cdc), queryCdpClaimsCmd(queryRoute, cdc),
queryHardClaimsCmd(queryRoute, cdc),
)...) )...)
return incentiveQueryCmd return incentiveQueryCmd
@ -35,16 +36,16 @@ const (
flagOwner = "owner" flagOwner = "owner"
) )
func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { func queryCdpClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "claims ", Use: "cdp-claims",
Short: "query USDX minting claims", Short: "query USDX minting claims",
Long: strings.TrimSpace( Long: strings.TrimSpace(
fmt.Sprintf(`Query USDX minting claims with optional flag for finding claims for a specifc owner fmt.Sprintf(`Query USDX minting claims with optional flag for finding claims for a specifc owner
Example: Example:
$ %s query %s claims $ %s query %s cdp-claims
$ %s query %s claims --owner kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw $ %s query %s cdp-claims --owner kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw
`, `,
version.ClientName, types.ModuleName, version.ClientName, types.ModuleName)), version.ClientName, types.ModuleName, version.ClientName, types.ModuleName)),
Args: cobra.NoArgs, Args: cobra.NoArgs,
@ -60,14 +61,67 @@ func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
params := types.NewQueryClaimsParams(page, limit, owner) params := types.NewQueryCdpClaimsParams(page, limit, owner)
bz, err := cdc.MarshalJSON(params) bz, err := cdc.MarshalJSON(params)
if err != nil { if err != nil {
return err return err
} }
// Query // Query
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetClaims) route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdpClaims)
res, height, err := cliCtx.QueryWithData(route, bz)
if err != nil {
return err
}
cliCtx = cliCtx.WithHeight(height)
var claims types.USDXMintingClaims
if err := cdc.UnmarshalJSON(res, &claims); err != nil {
return fmt.Errorf("failed to unmarshal claims: %w", err)
}
return cliCtx.PrintOutput(claims)
},
}
cmd.Flags().String(flagOwner, "", "(optional) filter by claim owner address")
cmd.Flags().Int(flags.FlagPage, 1, "pagination page of CDPs to to query for")
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of CDPs to query for")
return cmd
}
func queryHardClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "hard-claims",
Short: "query Hard liquidity provider claims",
Long: strings.TrimSpace(
fmt.Sprintf(`Query Hard liquidity provider claims with optional flag for finding claims for a specifc owner
Example:
$ %s query %s hard-claims
$ %s query %s hard-claims --owner kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw
`,
version.ClientName, types.ModuleName, version.ClientName, types.ModuleName)),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
strOwner := viper.GetString(flagOwner)
page := viper.GetInt(flags.FlagPage)
limit := viper.GetInt(flags.FlagLimit)
// Prepare params for querier
owner, err := sdk.AccAddressFromBech32(strOwner)
if err != nil {
return err
}
params := types.NewQueryHardClaimsParams(page, limit, owner)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetHardClaims)
res, height, err := cliCtx.QueryWithData(route, bz) res, height, err := cliCtx.QueryWithData(route, bz)
if err != nil { if err != nil {
return err return err

View File

@ -26,22 +26,23 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
} }
incentiveTxCmd.AddCommand(flags.PostCommands( incentiveTxCmd.AddCommand(flags.PostCommands(
getCmdClaim(cdc), getCmdClaimCdp(cdc),
getCmdClaimHard(cdc),
)...) )...)
return incentiveTxCmd return incentiveTxCmd
} }
func getCmdClaim(cdc *codec.Codec) *cobra.Command { func getCmdClaimCdp(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "claim [owner] [multiplier]", Use: "claim-cdp [owner] [multiplier]",
Short: "claim rewards for cdp owner and collateral-type", Short: "claim CDP rewards for cdp owner and collateral-type",
Long: strings.TrimSpace( Long: strings.TrimSpace(
fmt.Sprintf(`Claim any outstanding rewards owned by owner for the input collateral-type and multiplier, fmt.Sprintf(`Claim any outstanding CDP rewards owned by owner for the input collateral-type and multiplier,
Example: Example:
$ %s tx %s claim kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw large $ %s tx %s claim-cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw large
`, version.ClientName, types.ModuleName), `, version.ClientName, types.ModuleName),
), ),
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
@ -63,3 +64,34 @@ func getCmdClaim(cdc *codec.Codec) *cobra.Command {
}, },
} }
} }
func getCmdClaimHard(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "claim-hard [owner] [multiplier]",
Short: "claim Hard rewards for deposit/borrow and delegating",
Long: strings.TrimSpace(
fmt.Sprintf(`Claim owner's outstanding Hard rewards using given multiplier multiplier,
Example:
$ %s tx %s claim-hard kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw large
`, version.ClientName, types.ModuleName),
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
owner, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgClaimHardLiquidityProviderReward(owner, args[1])
err = msg.ValidateBasic()
if err != nil {
return err
}
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}

View File

@ -15,11 +15,12 @@ import (
) )
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(fmt.Sprintf("/%s/claims", types.ModuleName), queryClaimsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/cdp-claims", types.ModuleName), queryCdpClaimsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/%s/hard-claims", types.ModuleName), queryHardClaimsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/%s/parameters", types.ModuleName), queryParamsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/parameters", types.ModuleName), queryParamsHandlerFn(cliCtx)).Methods("GET")
} }
func queryClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { func queryCdpClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if err != nil { if err != nil {
@ -41,14 +42,54 @@ func queryClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
} }
} }
queryParams := types.NewQueryClaimsParams(page, limit, owner) queryParams := types.NewQueryCdpClaimsParams(page, limit, owner)
bz, err := cliCtx.Codec.MarshalJSON(queryParams) bz, err := cliCtx.Codec.MarshalJSON(queryParams)
if err != nil { if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err))
return return
} }
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/incentive/%s", types.QueryGetClaims), bz) res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/incentive/%s", types.QueryGetCdpClaims), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryHardClaimsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
var owner sdk.AccAddress
if x := r.URL.Query().Get(types.RestClaimOwner); len(x) != 0 {
ownerStr := strings.ToLower(strings.TrimSpace(x))
owner, err = sdk.AccAddressFromBech32(ownerStr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("cannot parse address from claim owner %s", ownerStr))
return
}
}
queryParams := types.NewQueryHardClaimsParams(page, limit, owner)
bz, err := cliCtx.Codec.MarshalJSON(queryParams)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err))
return
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/incentive/%s", types.QueryGetHardClaims), bz)
if err != nil { if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return return

View File

@ -16,10 +16,11 @@ import (
) )
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/incentive/claim", postClaimHandlerFn(cliCtx)).Methods("POST") r.HandleFunc("/incentive/claim-cdp", postClaimCdpHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc("/incentive/claim-hard", postClaimHardHandlerFn(cliCtx)).Methods("POST")
} }
func postClaimHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { func postClaimCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var requestBody types.PostClaimReq var requestBody types.PostClaimReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) { if !rest.ReadRESTReq(w, r, cliCtx.Codec, &requestBody) {
@ -51,3 +52,36 @@ func postClaimHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
utils.WriteGenerateStdTxResponse(w, cliCtx, requestBody.BaseReq, []sdk.Msg{msg}) 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 types.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.NewMsgClaimHardLiquidityProviderReward(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})
}
}

View File

@ -15,6 +15,8 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
switch msg := msg.(type) { switch msg := msg.(type) {
case types.MsgClaimUSDXMintingReward: case types.MsgClaimUSDXMintingReward:
return handleMsgClaimUSDXMintingReward(ctx, k, msg) return handleMsgClaimUSDXMintingReward(ctx, k, msg)
case types.MsgClaimHardLiquidityProviderReward:
return handleMsgClaimHardLiquidityProviderReward(ctx, k, msg)
default: default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
} }
@ -23,7 +25,18 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
func handleMsgClaimUSDXMintingReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimUSDXMintingReward) (*sdk.Result, error) { func handleMsgClaimUSDXMintingReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimUSDXMintingReward) (*sdk.Result, error) {
err := k.ClaimReward(ctx, msg.Sender, types.MultiplierName(msg.MultiplierName)) err := k.ClaimUSDXMintingReward(ctx, msg.Sender, types.MultiplierName(msg.MultiplierName))
if err != nil {
return nil, err
}
return &sdk.Result{
Events: ctx.EventManager().Events(),
}, nil
}
func handleMsgClaimHardLiquidityProviderReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimHardLiquidityProviderReward) (*sdk.Result, error) {
err := k.ClaimHardReward(ctx, msg.Sender, types.MultiplierName(msg.MultiplierName))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -63,7 +63,34 @@ func (suite *HandlerTestSuite) SetupTest() {
suite.ctx = ctx suite.ctx = ctx
} }
func (suite *HandlerTestSuite) addClaim() { 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) TestMsgHardLiquidityProviderClaimReward() {
suite.addHardLiquidityProviderClaim()
msg := incentive.NewMsgClaimHardLiquidityProviderReward(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())}
c1 := incentive.NewHardLiquidityProviderClaim(suite.addrs[0], c("ukava", 1000000), rewardPeriod, rewardPeriod, rewardPeriod)
suite.NotPanics(func() {
suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, c1)
})
}
func (suite *HandlerTestSuite) addUSDXMintingClaim() {
sk := suite.app.GetSupplyKeeper() sk := suite.app.GetSupplyKeeper()
err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000))) err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("ukava", 1000000000000)))
suite.Require().NoError(err) suite.Require().NoError(err)
@ -73,13 +100,6 @@ func (suite *HandlerTestSuite) addClaim() {
}) })
} }
func (suite *HandlerTestSuite) TestMsgClaimReward() {
suite.addClaim()
msg := incentive.NewMsgClaimUSDXMintingReward(suite.addrs[0], "small")
res, err := suite.handler(suite.ctx, msg)
suite.NoError(err)
suite.Require().NotNil(res)
}
func TestHandlerTestSuite(t *testing.T) { func TestHandlerTestSuite(t *testing.T) {
suite.Run(t, new(HandlerTestSuite)) suite.Run(t, new(HandlerTestSuite))
} }

View File

@ -12,8 +12,8 @@ import (
validatorvesting "github.com/kava-labs/kava/x/validator-vesting" validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
) )
// ClaimReward sends the reward amount to the input address and zero's out the claim in the store // ClaimUSDXMintingReward sends the reward amount to the input address and zero's out the claim in the store
func (k Keeper) ClaimReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error { func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
claim, found := k.GetUSDXMintingClaim(ctx, addr) claim, found := k.GetUSDXMintingClaim(ctx, addr)
if !found { if !found {
return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr) return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
@ -30,7 +30,7 @@ func (k Keeper) ClaimReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName
return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd) return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), claimEnd)
} }
claim, err := k.SynchronizeClaim(ctx, claim) claim, err := k.SynchronizeUSDXMintingClaim(ctx, claim)
if err != nil { if err != nil {
return err return err
} }
@ -47,13 +47,64 @@ func (k Keeper) ClaimReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName
return err return err
} }
k.ZeroClaim(ctx, claim) k.ZeroUSDXMintingClaim(ctx, claim)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
types.EventTypeClaim, types.EventTypeClaim,
sdk.NewAttribute(types.AttributeKeyClaimedBy, addr.String()), sdk.NewAttribute(types.AttributeKeyClaimedBy, claim.GetOwner().String()),
sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.Reward.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)
}
rewardAmount := claim.Reward.Amount.ToDec().Mul(multiplier.Factor).RoundInt()
if rewardAmount.IsZero() {
return types.ErrZeroClaim
}
rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount)
length := ctx.BlockTime().AddDate(0, int(multiplier.MonthsLockup), 0).Unix() - ctx.BlockTime().Unix()
err := k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(rewardCoin), 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 return nil

View File

@ -14,12 +14,13 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
cdptypes "github.com/kava-labs/kava/x/cdp/types" cdptypes "github.com/kava-labs/kava/x/cdp/types"
"github.com/kava-labs/kava/x/hard"
"github.com/kava-labs/kava/x/incentive/types" "github.com/kava-labs/kava/x/incentive/types"
"github.com/kava-labs/kava/x/kavadist" "github.com/kava-labs/kava/x/kavadist"
validatorvesting "github.com/kava-labs/kava/x/validator-vesting" validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
) )
func (suite *KeeperTestSuite) TestPayoutClaim() { func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
type args struct { type args struct {
ctype string ctype string
rewardsPerSecond sdk.Coin rewardsPerSecond sdk.Coin
@ -129,7 +130,7 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod) err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
suite.Require().NoError(err) suite.Require().NoError(err)
err = suite.keeper.ClaimReward(suite.ctx, suite.addrs[0], tc.args.multiplier) err = suite.keeper.ClaimUSDXMintingReward(suite.ctx, suite.addrs[0], tc.args.multiplier)
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)
@ -155,6 +156,173 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
} }
} }
func (suite *KeeperTestSuite) TestPayoutHardLiquidityProviderClaim() {
type args struct {
deposit sdk.Coins
borrow sdk.Coins
rewardsPerSecond sdk.Coin
initialTime time.Time
multipliers types.Multipliers
multiplier types.MultiplierName
timeElapsed int64
expectedReward sdk.Coin
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{
deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: c("hard", 122354),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
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,
expectedReward: c("hard", 21142771200), // 10571385600 (deposit reward) + 10571385600 (borrow reward)
expectedPeriods: vesting.Periods{vesting.Period{Length: 31536000, Amount: cs(c("hard", 21142771200))}},
isPeriodicVestingAccount: true,
},
errArgs{
expectPass: true,
contains: "",
},
},
{
"invalid zero rewards",
args{
deposit: cs(c("bnb", 10000000000)),
borrow: cs(c("bnb", 5000000000)),
rewardsPerSecond: c("hard", 0),
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
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,
expectedReward: sdk.Coin{},
expectedPeriods: vesting.Periods{},
isPeriodicVestingAccount: false,
},
errArgs{
expectPass: false,
contains: "claim amount rounds to zero",
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupWithGenState()
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
// setup kavadist state
sk := suite.app.GetSupplyKeeper()
err := sk.MintCoins(suite.ctx, kavadist.ModuleName, cs(c("hard", 1000000000000)))
suite.Require().NoError(err)
// Set up generic reward periods
var rewardPeriods types.RewardPeriods
for _, coin := range tc.args.deposit {
rewardPeriod := types.NewRewardPeriod(true, coin.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
rewardPeriods = append(rewardPeriods, rewardPeriod)
}
// Set up incentive state
params := types.NewParams(
rewardPeriods, rewardPeriods, rewardPeriods, rewardPeriods,
tc.args.multipliers,
tc.args.initialTime.Add(time.Hour*24*365*5),
)
suite.keeper.SetParams(suite.ctx, params)
// Set each denom's previous accrual time and supply reward factor
for _, coin := range tc.args.deposit {
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
suite.keeper.SetHardSupplyRewardFactor(suite.ctx, coin.Denom, sdk.ZeroDec())
}
for _, coin := range tc.args.borrow {
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
suite.keeper.SetHardBorrowRewardFactor(suite.ctx, coin.Denom, sdk.ZeroDec())
}
hardKeeper := suite.app.GetHardKeeper()
userAddr := suite.addrs[3]
// User deposits
err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.deposit)
suite.Require().NoError(err)
// User borrows
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, suite.addrs[3])
suite.Require().True(found)
suite.Require().Equal(sdk.ZeroInt(), claim.Reward.Amount)
// 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.GetHardSupplyRewardPeriod(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.GetHardBorrowRewardPeriod(runCtx, coin.Denom)
suite.Require().True(found)
err = suite.keeper.AccumulateHardBorrowRewards(runCtx, rewardPeriod)
suite.Require().NoError(err)
}
// Fetch pre-claim balances
ak := suite.app.GetAccountKeeper()
preClaimAcc := ak.GetAccount(runCtx, suite.addrs[3])
err = suite.keeper.ClaimHardReward(runCtx, suite.addrs[3], 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[3])
suite.Require().Equal(preClaimAcc.GetCoins().Add(tc.args.expectedReward), 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 the claim's reward amount has been reset to 0
claim, found := suite.keeper.GetHardLiquidityProviderClaim(runCtx, suite.addrs[3])
suite.Require().True(found)
suite.Require().Equal(c("hard", 0), claim.Reward)
} else {
suite.Require().Error(err)
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
}
})
}
}
func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() { func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() {
type accountArgs struct { type accountArgs struct {
periods vesting.Periods periods vesting.Periods

View File

@ -17,8 +17,10 @@ func NewQuerier(k Keeper) sdk.Querier {
switch path[0] { switch path[0] {
case types.QueryGetParams: case types.QueryGetParams:
return queryGetParams(ctx, req, k) return queryGetParams(ctx, req, k)
case types.QueryGetClaims: case types.QueryGetCdpClaims:
return queryGetClaims(ctx, req, k) return queryGetCdpClaims(ctx, req, k)
case types.QueryGetHardClaims:
return queryGetHardClaims(ctx, req, k)
default: default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
} }
@ -38,8 +40,8 @@ func queryGetParams(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
return bz, nil return bz, nil
} }
func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { func queryGetCdpClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
var requestParams types.QueryClaimsParams var requestParams types.QueryCdpClaimsParams
err := k.cdc.UnmarshalJSON(req.Data, &requestParams) err := k.cdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
@ -67,3 +69,33 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
} }
return bz, nil return bz, nil
} }
func queryGetHardClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
var requestParams types.QueryHardClaimsParams
err := k.cdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
var claims types.HardLiquidityProviderClaims
if len(requestParams.Owner) > 0 {
claim, _ := k.GetHardLiquidityProviderClaim(ctx, requestParams.Owner)
claims = append(claims, claim)
} else {
claims = k.GetAllHardLiquidityProviderClaims(ctx)
}
var paginatedClaims types.HardLiquidityProviderClaims
start, end := client.Paginate(len(claims), requestParams.Page, requestParams.Limit, 100)
if start < 0 || end < 0 {
paginatedClaims = types.HardLiquidityProviderClaims{}
} else {
paginatedClaims = claims[start:end]
}
bz, err := codec.MarshalJSONIndent(k.cdc, paginatedClaims)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}

View File

@ -518,16 +518,16 @@ func (k Keeper) InitializeHardDelegatorReward(ctx sdk.Context, delegator sdk.Acc
k.SetHardLiquidityProviderClaim(ctx, claim) k.SetHardLiquidityProviderClaim(ctx, claim)
} }
// ZeroClaim zeroes out the claim object's rewards and returns the updated claim object // ZeroUSDXMintingClaim zeroes out the claim object's rewards and returns the updated claim object
func (k Keeper) ZeroClaim(ctx sdk.Context, claim types.USDXMintingClaim) types.USDXMintingClaim { func (k Keeper) ZeroUSDXMintingClaim(ctx sdk.Context, claim types.USDXMintingClaim) types.USDXMintingClaim {
claim.Reward = sdk.NewCoin(claim.Reward.Denom, sdk.ZeroInt()) claim.Reward = sdk.NewCoin(claim.Reward.Denom, sdk.ZeroInt())
k.SetUSDXMintingClaim(ctx, claim) k.SetUSDXMintingClaim(ctx, claim)
return claim return claim
} }
// SynchronizeClaim updates the claim object by adding any rewards that have accumulated. // SynchronizeUSDXMintingClaim updates the claim object by adding any rewards that have accumulated.
// Returns the updated claim object // Returns the updated claim object
func (k Keeper) SynchronizeClaim(ctx sdk.Context, claim types.USDXMintingClaim) (types.USDXMintingClaim, error) { func (k Keeper) SynchronizeUSDXMintingClaim(ctx sdk.Context, claim types.USDXMintingClaim) (types.USDXMintingClaim, error) {
for _, ri := range claim.RewardIndexes { for _, ri := range claim.RewardIndexes {
cdp, found := k.cdpKeeper.GetCdpByOwnerAndCollateralType(ctx, claim.Owner, ri.CollateralType) cdp, found := k.cdpKeeper.GetCdpByOwnerAndCollateralType(ctx, claim.Owner, ri.CollateralType)
if !found { if !found {
@ -546,6 +546,31 @@ func (k Keeper) synchronizeRewardAndReturnClaim(ctx sdk.Context, cdp cdptypes.CD
return claim return claim
} }
// SynchronizeHardLiquidityProviderClaim adds any accumulated rewards
func (k Keeper) SynchronizeHardLiquidityProviderClaim(ctx sdk.Context, owner sdk.AccAddress) {
// Synchronize any hard liquidity supply-side rewards
deposit, foundDeposit := k.hardKeeper.GetDeposit(ctx, owner)
if foundDeposit {
k.SynchronizeHardSupplyReward(ctx, deposit)
}
// Synchronize any hard liquidity borrow-side rewards
borrow, foundBorrow := k.hardKeeper.GetBorrow(ctx, owner)
if foundBorrow {
k.SynchronizeHardBorrowReward(ctx, borrow)
}
// Synchronize any hard delegator rewards
k.SynchronizeHardDelegatorRewards(ctx, owner)
}
// ZeroHardLiquidityProviderClaim zeroes out the claim object's rewards and returns the updated claim object
func (k Keeper) ZeroHardLiquidityProviderClaim(ctx sdk.Context, claim types.HardLiquidityProviderClaim) types.HardLiquidityProviderClaim {
claim.Reward = sdk.NewCoin(claim.Reward.Denom, sdk.ZeroInt())
k.SetHardLiquidityProviderClaim(ctx, claim)
return claim
}
// CalculateTimeElapsed calculates the number of reward-eligible seconds that have passed since the previous // CalculateTimeElapsed calculates the number of reward-eligible seconds that have passed since the previous
// time rewards were accrued, taking into account the end time of the reward period // time rewards were accrued, taking into account the end time of the reward period
func CalculateTimeElapsed(rewardPeriod types.RewardPeriod, blockTime time.Time, previousAccrualTime time.Time) sdk.Int { func CalculateTimeElapsed(rewardPeriod types.RewardPeriod, blockTime time.Time, previousAccrualTime time.Time) sdk.Int {

View File

@ -20,4 +20,5 @@ func RegisterCodec(cdc *codec.Codec) {
// Register msgs // Register msgs
cdc.RegisterConcrete(MsgClaimUSDXMintingReward{}, "incentive/MsgClaimUSDXMintingReward", nil) cdc.RegisterConcrete(MsgClaimUSDXMintingReward{}, "incentive/MsgClaimUSDXMintingReward", nil)
cdc.RegisterConcrete(MsgClaimHardLiquidityProviderReward{}, "incentive/MsgClaimHardLiquidityProviderReward", nil)
} }

View File

@ -10,6 +10,7 @@ const (
AttributeValueCategory = ModuleName AttributeValueCategory = ModuleName
AttributeKeyClaimedBy = "claimed_by" AttributeKeyClaimedBy = "claimed_by"
AttributeKeyClaimAmount = "claim_amount" AttributeKeyClaimAmount = "claim_amount"
AttributeKeyClaimType = "claim_type"
AttributeKeyRewardPeriod = "reward_period" AttributeKeyRewardPeriod = "reward_period"
AttributeKeyClaimPeriod = "claim_period" AttributeKeyClaimPeriod = "claim_period"
) )

View File

@ -33,6 +33,8 @@ type CdpKeeper interface {
// HardKeeper defines the expected hard keeper for interacting with Hard protocol // HardKeeper defines the expected hard keeper for interacting with Hard protocol
type HardKeeper interface { type HardKeeper interface {
GetDeposit(ctx sdk.Context, depositor sdk.AccAddress) (hardtypes.Deposit, bool)
GetBorrow(ctx sdk.Context, borrower sdk.AccAddress) (hardtypes.Borrow, bool)
GetSupplyInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool) GetSupplyInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool)
GetBorrowInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool) GetBorrowInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool)
GetBorrowedCoins(ctx sdk.Context) (coins sdk.Coins, found bool) GetBorrowedCoins(ctx sdk.Context) (coins sdk.Coins, found bool)

View File

@ -9,8 +9,9 @@ import (
// ensure Msg interface compliance at compile time // ensure Msg interface compliance at compile time
var _ sdk.Msg = &MsgClaimUSDXMintingReward{} var _ sdk.Msg = &MsgClaimUSDXMintingReward{}
var _ sdk.Msg = &MsgClaimHardLiquidityProviderReward{}
// MsgClaimUSDXMintingReward message type used to claim rewards // MsgClaimUSDXMintingReward message type used to claim USDX minting rewards
type MsgClaimUSDXMintingReward struct { type MsgClaimUSDXMintingReward struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"` Sender sdk.AccAddress `json:"sender" yaml:"sender"`
MultiplierName string `json:"multiplier_name" yaml:"multiplier_name"` MultiplierName string `json:"multiplier_name" yaml:"multiplier_name"`
@ -28,7 +29,7 @@ func NewMsgClaimUSDXMintingReward(sender sdk.AccAddress, multiplierName string)
func (msg MsgClaimUSDXMintingReward) Route() string { return RouterKey } func (msg MsgClaimUSDXMintingReward) Route() string { return RouterKey }
// Type returns a human-readable string for the message, intended for utilization within tags. // Type returns a human-readable string for the message, intended for utilization within tags.
func (msg MsgClaimUSDXMintingReward) Type() string { return "claim_reward" } func (msg MsgClaimUSDXMintingReward) Type() string { return "claim_usdx_minting_reward" }
// ValidateBasic does a simple validation check that doesn't require access to state. // ValidateBasic does a simple validation check that doesn't require access to state.
func (msg MsgClaimUSDXMintingReward) ValidateBasic() error { func (msg MsgClaimUSDXMintingReward) ValidateBasic() error {
@ -48,3 +49,44 @@ func (msg MsgClaimUSDXMintingReward) GetSignBytes() []byte {
func (msg MsgClaimUSDXMintingReward) GetSigners() []sdk.AccAddress { func (msg MsgClaimUSDXMintingReward) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Sender} return []sdk.AccAddress{msg.Sender}
} }
// MsgClaimHardLiquidityProviderReward message type used to claim Hard liquidity provider rewards
type MsgClaimHardLiquidityProviderReward struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
MultiplierName string `json:"multiplier_name" yaml:"multiplier_name"`
}
// NewMsgClaimHardLiquidityProviderReward returns a new MsgClaimHardLiquidityProviderReward.
func NewMsgClaimHardLiquidityProviderReward(sender sdk.AccAddress, multiplierName string) MsgClaimHardLiquidityProviderReward {
return MsgClaimHardLiquidityProviderReward{
Sender: sender,
MultiplierName: multiplierName,
}
}
// Route return the message type used for routing the message.
func (msg MsgClaimHardLiquidityProviderReward) Route() string { return RouterKey }
// Type returns a human-readable string for the message, intended for utilization within tags.
func (msg MsgClaimHardLiquidityProviderReward) Type() string {
return "claim_hard_liquidity_provider_reward"
}
// ValidateBasic does a simple validation check that doesn't require access to state.
func (msg MsgClaimHardLiquidityProviderReward) ValidateBasic() error {
if msg.Sender.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
}
return MultiplierName(strings.ToLower(msg.MultiplierName)).IsValid()
}
// GetSignBytes gets the canonical byte representation of the Msg.
func (msg MsgClaimHardLiquidityProviderReward) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}
// GetSigners returns the addresses of signers that must sign.
func (msg MsgClaimHardLiquidityProviderReward) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Sender}
}

View File

@ -7,7 +7,8 @@ import (
// Querier routes for the incentive module // Querier routes for the incentive module
const ( const (
QueryGetClaims = "claims" QueryGetCdpClaims = "cdp-claims"
QueryGetHardClaims = "hard-claims"
RestClaimOwner = "owner" RestClaimOwner = "owner"
RestClaimCollateralType = "collateral_type" RestClaimCollateralType = "collateral_type"
QueryGetParams = "parameters" QueryGetParams = "parameters"
@ -15,16 +16,32 @@ const (
QueryGetClaimPeriods = "claim-periods" QueryGetClaimPeriods = "claim-periods"
) )
// QueryClaimsParams params for query /incentive/claims // QueryCdpClaimsParams params for query /incentive/claims
type QueryClaimsParams struct { type QueryCdpClaimsParams struct {
Page int `json:"page" yaml:"page"` Page int `json:"page" yaml:"page"`
Limit int `json:"limit" yaml:"limit"` Limit int `json:"limit" yaml:"limit"`
Owner sdk.AccAddress Owner sdk.AccAddress
} }
// NewQueryClaimsParams returns QueryClaimsParams // NewQueryCdpClaimsParams returns QueryCdpClaimsParams
func NewQueryClaimsParams(page, limit int, owner sdk.AccAddress) QueryClaimsParams { func NewQueryCdpClaimsParams(page, limit int, owner sdk.AccAddress) QueryCdpClaimsParams {
return QueryClaimsParams{ return QueryCdpClaimsParams{
Page: page,
Limit: limit,
Owner: owner,
}
}
// QueryHardClaimsParams params for query /incentive/claims
type QueryHardClaimsParams struct {
Page int `json:"page" yaml:"page"`
Limit int `json:"limit" yaml:"limit"`
Owner sdk.AccAddress
}
// NewQueryHardClaimsParams returns QueryHardClaimsParams
func NewQueryHardClaimsParams(page, limit int, owner sdk.AccAddress) QueryHardClaimsParams {
return QueryHardClaimsParams{
Page: page, Page: page,
Limit: limit, Limit: limit,
Owner: owner, Owner: owner,