// nolint package cli import ( "fmt" "strings" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) var ( flagOnlyFromValidator = "only-from-validator" flagIsValidator = "is-validator" flagComission = "commission" flagMaxMessagesPerTx = "max-msgs" ) const ( MaxMessagesPerTxDefault = 5 ) // GetTxCmd returns the transaction commands for this module func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { distTxCmd := &cobra.Command{ Use: types.ModuleName, Short: "Distribution transactions subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } distTxCmd.AddCommand(client.PostCommands( GetCmdWithdrawRewards(cdc), GetCmdSetWithdrawAddr(cdc), GetCmdWithdrawAllRewards(cdc, storeKey), )...) return distTxCmd } type generateOrBroadcastFunc func(context.CLIContext, auth.TxBuilder, []sdk.Msg) error func splitAndApply( generateOrBroadcast generateOrBroadcastFunc, cliCtx context.CLIContext, txBldr auth.TxBuilder, msgs []sdk.Msg, chunkSize int, ) error { if chunkSize == 0 { return generateOrBroadcast(cliCtx, txBldr, msgs) } // split messages into slices of length chunkSize totalMessages := len(msgs) for i := 0; i < len(msgs); i += chunkSize { sliceEnd := i + chunkSize if sliceEnd > totalMessages { sliceEnd = totalMessages } msgChunk := msgs[i:sliceEnd] if err := generateOrBroadcast(cliCtx, txBldr, msgChunk); err != nil { return err } } return nil } // command to withdraw rewards func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-rewards [validator-addr]", Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", Long: strings.TrimSpace( fmt.Sprintf(`Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator. Example: $ %s tx distr withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey $ %s tx distr withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission `, version.ClientName, version.ClientName, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} if viper.GetBool(flagComission) { msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs) }, } cmd.Flags().Bool(flagComission, false, "also withdraw validator's commission") return cmd } // command to withdraw all rewards func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-all-rewards", Short: "withdraw all delegations rewards for a delegator", Long: strings.TrimSpace( fmt.Sprintf(`Withdraw all rewards for a single delegator. Example: $ %s tx distr withdraw-all-rewards --from mykey `, version.ClientName, ), ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) delAddr := cliCtx.GetFromAddress() msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr) if err != nil { return err } chunkSize := viper.GetInt(flagMaxMessagesPerTx) return splitAndApply(utils.GenerateOrBroadcastMsgs, cliCtx, txBldr, msgs, chunkSize) }, } cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") return cmd } // command to replace a delegator's withdrawal address func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "set-withdraw-addr [withdraw-addr]", Short: "change the default withdraw address for rewards associated with an address", Long: strings.TrimSpace( fmt.Sprintf(`Set the withdraw address for rewards associated with a delegator address. Example: $ %s tx set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey `, version.ClientName, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) delAddr := cliCtx.GetFromAddress() withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } } // GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "community-pool-spend [proposal-file]", Args: cobra.ExactArgs(1), Short: "Submit a community pool spend proposal", Long: strings.TrimSpace( fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit. The proposal details must be supplied via a JSON file. Example: $ %s tx gov submit-proposal community-pool-spend --from= Where proposal.json contains: { "title": "Community Pool Spend", "description": "Pay me some Atoms!", "recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", "amount": [ { "denom": "stake", "amount": "10000" } ], "deposit": [ { "denom": "stake", "amount": "10000" } ] } `, version.ClientName, ), ), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) proposal, err := ParseCommunityPoolSpendProposalJSON(cdc, args[0]) if err != nil { return err } from := cliCtx.GetFromAddress() content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, proposal.Amount) msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from) if err := msg.ValidateBasic(); err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } return cmd }