Packages named utils, common, or misc provide clients with no sense of what the package contains. This makes it harder for clients to use the package and makes it harder for maintainers to keep the package focused. Over time, they accumulate dependencies that can make compilation significantly and unnecessarily slower, especially in large programs. And since such package names are generic, they are more likely to collide with other packages imported by client code, forcing clients to invent names to distinguish them. cit. https://blog.golang.org/package-names
301 lines
8.8 KiB
Go
301 lines
8.8 KiB
Go
// nolint
|
|
package cli
|
|
|
|
import (
|
|
"bufio"
|
|
"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/client/flags"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/version"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
|
"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"
|
|
flagCommission = "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(flags.PostCommands(
|
|
GetCmdWithdrawRewards(cdc),
|
|
GetCmdSetWithdrawAddr(cdc),
|
|
GetCmdWithdrawAllRewards(cdc, storeKey),
|
|
GetCmdFundCommunityPool(cdc),
|
|
)...)
|
|
|
|
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 distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey
|
|
$ %s tx distribution withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission
|
|
`,
|
|
version.ClientName, version.ClientName,
|
|
),
|
|
),
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
|
|
cliCtx := context.NewCLIContextWithInput(inBuf).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(flagCommission) {
|
|
msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr))
|
|
}
|
|
|
|
return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs)
|
|
},
|
|
}
|
|
cmd.Flags().Bool(flagCommission, 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 distribution withdraw-all-rewards --from mykey
|
|
`,
|
|
version.ClientName,
|
|
),
|
|
),
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
|
|
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
|
|
|
delAddr := cliCtx.GetFromAddress()
|
|
|
|
// The transaction cannot be generated offline since it requires a query
|
|
// to get all the validators.
|
|
if cliCtx.GenerateOnly {
|
|
return fmt.Errorf("command disabled with the provided flag: %s", flags.FlagGenerateOnly)
|
|
}
|
|
|
|
msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
chunkSize := viper.GetInt(flagMaxMessagesPerTx)
|
|
return splitAndApply(authclient.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 distribution set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey
|
|
`,
|
|
version.ClientName,
|
|
),
|
|
),
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
|
|
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
|
|
|
delAddr := cliCtx.GetFromAddress()
|
|
withdrawAddr, err := sdk.AccAddressFromBech32(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
|
|
return authclient.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 <path/to/proposal.json> --from=<key_or_address>
|
|
|
|
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 {
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
|
|
cliCtx := context.NewCLIContextWithInput(inBuf).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 authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// GetCmdFundCommunityPool returns a command implementation that supports directly
|
|
// funding the community pool.
|
|
func GetCmdFundCommunityPool(cdc *codec.Codec) *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "fund-community-pool [amount]",
|
|
Args: cobra.ExactArgs(1),
|
|
Short: "Funds the community pool with the specified amount",
|
|
Long: strings.TrimSpace(
|
|
fmt.Sprintf(`Funds the community pool with the specified amount
|
|
|
|
Example:
|
|
$ %s tx distribution fund-community-pool 100uatom --from mykey
|
|
`,
|
|
version.ClientName,
|
|
),
|
|
),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
|
|
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
|
|
|
depositorAddr := cliCtx.GetFromAddress()
|
|
amount, err := sdk.ParseCoins(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := types.NewMsgFundCommunityPool(amount, depositorAddr)
|
|
if err := msg.ValidateBasic(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
|
},
|
|
}
|
|
}
|