## Description Closes: #9660 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
702 lines
18 KiB
Go
702 lines
18 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/version"
|
|
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
|
"github.com/cosmos/cosmos-sdk/x/group"
|
|
)
|
|
|
|
const (
|
|
FlagExec = "exec"
|
|
ExecTry = "try"
|
|
)
|
|
|
|
// TxCmd returns a root CLI command handler for all x/group transaction commands.
|
|
func TxCmd(name string) *cobra.Command {
|
|
txCmd := &cobra.Command{
|
|
Use: name,
|
|
Short: "Group transaction subcommands",
|
|
DisableFlagParsing: true,
|
|
SuggestionsMinimumDistance: 2,
|
|
RunE: client.ValidateCmd,
|
|
}
|
|
|
|
txCmd.AddCommand(
|
|
MsgCreateGroupCmd(),
|
|
MsgUpdateGroupAdminCmd(),
|
|
MsgUpdateGroupMetadataCmd(),
|
|
MsgUpdateGroupMembersCmd(),
|
|
MsgCreateGroupPolicyCmd(),
|
|
MsgUpdateGroupPolicyAdminCmd(),
|
|
MsgUpdateGroupPolicyDecisionPolicyCmd(),
|
|
MsgUpdateGroupPolicyMetadataCmd(),
|
|
MsgCreateProposalCmd(),
|
|
MsgVoteCmd(),
|
|
MsgExecCmd(),
|
|
)
|
|
|
|
return txCmd
|
|
}
|
|
|
|
// MsgCreateGroupCmd creates a CLI command for Msg/CreateGroup.
|
|
func MsgCreateGroupCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "create-group [admin] [metadata] [members-json-file]",
|
|
Short: "Create a group which is an aggregation " +
|
|
"of member accounts with associated weights and " +
|
|
"an administrator account. Note, the '--from' flag is " +
|
|
"ignored as it is implied from [admin].",
|
|
Long: strings.TrimSpace(
|
|
fmt.Sprintf(`Create a group which is an aggregation of member accounts with associated weights and
|
|
an administrator account. Note, the '--from' flag is ignored as it is implied from [admin].
|
|
Members accounts can be given through a members JSON file that contains an array of members.
|
|
|
|
Example:
|
|
$ %s tx group create-group [admin] [metadata] [members-json-file]
|
|
|
|
Where members.json contains:
|
|
|
|
{
|
|
"members": [
|
|
{
|
|
"address": "addr1",
|
|
"weight": "1",
|
|
"metadata": "some metadata"
|
|
},
|
|
{
|
|
"address": "addr2",
|
|
"weight": "1",
|
|
"metadata": "some metadata"
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
version.AppName,
|
|
),
|
|
),
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
members, err := parseMembers(clientCtx, args[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
metadata, err := base64.StdEncoding.DecodeString(args[1])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
msg := &group.MsgCreateGroup{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
Members: members,
|
|
Metadata: metadata,
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupMembersCmd creates a CLI command for Msg/UpdateGroupMembers.
|
|
func MsgUpdateGroupMembersCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-members [admin] [group-id] [members-json-file]",
|
|
Short: "Update a group's members. Set a member's weight to \"0\" to delete it.",
|
|
Long: strings.TrimSpace(
|
|
fmt.Sprintf(`Update a group's members
|
|
|
|
Example:
|
|
$ %s tx group update-group-members [admin] [group-id] [members-json-file]
|
|
|
|
Where members.json contains:
|
|
|
|
{
|
|
"members": [
|
|
{
|
|
"address": "addr1",
|
|
"weight": "1",
|
|
"metadata": "some new metadata"
|
|
},
|
|
{
|
|
"address": "addr2",
|
|
"weight": "0",
|
|
"metadata": "some metadata"
|
|
}
|
|
]
|
|
}
|
|
|
|
Set a member's weight to "0" to delete it.
|
|
`,
|
|
version.AppName,
|
|
),
|
|
),
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
members, err := parseMembers(clientCtx, args[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
groupID, err := strconv.ParseUint(args[1], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := &group.MsgUpdateGroupMembers{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
MemberUpdates: members,
|
|
GroupId: groupID,
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupAdminCmd creates a CLI command for Msg/UpdateGroupAdmin.
|
|
func MsgUpdateGroupAdminCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-admin [admin] [group-id] [new-admin]",
|
|
Short: "Update a group's admin",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
groupID, err := strconv.ParseUint(args[1], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := &group.MsgUpdateGroupAdmin{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
NewAdmin: args[2],
|
|
GroupId: groupID,
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupMetadataCmd creates a CLI command for Msg/UpdateGroupMetadata.
|
|
func MsgUpdateGroupMetadataCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-metadata [admin] [group-id] [metadata]",
|
|
Short: "Update a group's metadata",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
groupID, err := strconv.ParseUint(args[1], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := base64.StdEncoding.DecodeString(args[2])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
msg := &group.MsgUpdateGroupMetadata{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
Metadata: b,
|
|
GroupId: groupID,
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgCreateGroupPolicyCmd creates a CLI command for Msg/CreateGroupPolicy.
|
|
func MsgCreateGroupPolicyCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "create-group-policy [admin] [group-id] [metadata] [decision-policy]",
|
|
Short: "Create a group policy which is an account " +
|
|
"associated with a group and a decision policy. " +
|
|
"Note, the '--from' flag is " +
|
|
"ignored as it is implied from [admin].",
|
|
Long: strings.TrimSpace(
|
|
fmt.Sprintf(`Create a group policy which is an account associated with a group and a decision policy.
|
|
Note, the '--from' flag is ignored as it is implied from [admin].
|
|
|
|
Example:
|
|
$ %s tx group create-group-policy [admin] [group-id] [metadata] \
|
|
'{"@type":"/cosmos.group.v1beta1.ThresholdDecisionPolicy", "threshold":"1", "timeout":"1s"}'
|
|
`,
|
|
version.AppName,
|
|
),
|
|
),
|
|
Args: cobra.ExactArgs(4),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
groupID, err := strconv.ParseUint(args[1], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var policy group.DecisionPolicy
|
|
if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(args[3]), &policy); err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := base64.StdEncoding.DecodeString(args[2])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
msg, err := group.NewMsgCreateGroupPolicy(
|
|
clientCtx.GetFromAddress(),
|
|
groupID,
|
|
b,
|
|
policy,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupPolicyAdminCmd creates a CLI command for Msg/UpdateGroupPolicyAdmin.
|
|
func MsgUpdateGroupPolicyAdminCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-policy-admin [admin] [group-policy-account] [new-admin]",
|
|
Short: "Update a group policy admin",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := &group.MsgUpdateGroupPolicyAdmin{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
Address: args[1],
|
|
NewAdmin: args[2],
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupPolicyDecisionPolicyCmd creates a CLI command for Msg/UpdateGroupPolicyDecisionPolicy.
|
|
func MsgUpdateGroupPolicyDecisionPolicyCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-policy-decision-policy [admin] [group-policy-account] [decision-policy]",
|
|
Short: "Update a group policy's decision policy",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var policy group.DecisionPolicy
|
|
if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(args[2]), &policy); err != nil {
|
|
return err
|
|
}
|
|
|
|
accountAddress, err := sdk.AccAddressFromBech32(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(
|
|
clientCtx.GetFromAddress(),
|
|
accountAddress,
|
|
policy,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgUpdateGroupPolicyMetadataCmd creates a CLI command for Msg/MsgUpdateGroupPolicyMetadata.
|
|
func MsgUpdateGroupPolicyMetadataCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "update-group-policy-metadata [admin] [group-policy-account] [new-metadata]",
|
|
Short: "Update a group policy metadata",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := base64.StdEncoding.DecodeString(args[2])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
msg := &group.MsgUpdateGroupPolicyMetadata{
|
|
Admin: clientCtx.GetFromAddress().String(),
|
|
Address: args[1],
|
|
Metadata: b,
|
|
}
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgCreateProposalCmd creates a CLI command for Msg/CreateProposal.
|
|
func MsgCreateProposalCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "create-proposal [group-policy-account] [proposer[,proposer]*] [msg_tx_json_file] [metadata]",
|
|
Short: "Submit a new proposal",
|
|
Long: `Submit a new proposal.
|
|
|
|
Parameters:
|
|
group-policy-account: account address of the group policy
|
|
proposer: comma separated (no spaces) list of proposer account addresses. Example: "addr1,addr2"
|
|
Metadata: metadata for the proposal
|
|
msg_tx_json_file: path to json file with messages that will be executed if the proposal is accepted.
|
|
`,
|
|
Args: cobra.ExactArgs(4),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
proposers := strings.Split(args[1], ",")
|
|
for i := range proposers {
|
|
proposers[i] = strings.TrimSpace(proposers[i])
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
theTx, err := authclient.ReadTxFromFile(clientCtx, args[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
msgs := theTx.GetMsgs()
|
|
|
|
b, err := base64.StdEncoding.DecodeString(args[3])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
execStr, _ := cmd.Flags().GetString(FlagExec)
|
|
|
|
msg, err := group.NewMsgCreateProposalRequest(
|
|
args[0],
|
|
proposers,
|
|
msgs,
|
|
b,
|
|
execFromString(execStr),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
cmd.Flags().String(FlagExec, "", "Set to 1 to try to execute proposal immediately after creation (proposers signatures are considered as Yes votes)")
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgWithdrawProposalCmd creates a CLI command for Msg/WithdrawProposal.
|
|
func MsgWithdrawProposalCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "withdraw-proposal [proposal-id] [group-policy-admin-or-proposer]",
|
|
Short: "Withdraw a submitted proposal",
|
|
Long: `Withdraw a submitted proposal.
|
|
|
|
Parameters:
|
|
proposal-id: unique ID of the proposal.
|
|
group-policy-admin-or-proposer: either admin of the group policy or one the proposer of the proposal.
|
|
(note: --from flag will be ignored here)
|
|
`,
|
|
Args: cobra.ExactArgs(2),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
proposalID, err := strconv.ParseUint(args[0], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := &group.MsgWithdrawProposal{
|
|
ProposalId: proposalID,
|
|
Address: clientCtx.GetFromAddress().String(),
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgVoteCmd creates a CLI command for Msg/Vote.
|
|
func MsgVoteCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "vote [proposal-id] [voter] [choice] [metadata]",
|
|
Short: "Vote on a proposal",
|
|
Long: `Vote on a proposal.
|
|
|
|
Parameters:
|
|
proposal-id: unique ID of the proposal
|
|
voter: voter account addresses.
|
|
choice: choice of the voter(s)
|
|
CHOICE_UNSPECIFIED: no-op
|
|
CHOICE_NO: no
|
|
CHOICE_YES: yes
|
|
CHOICE_ABSTAIN: abstain
|
|
CHOICE_VETO: veto
|
|
Metadata: metadata for the vote
|
|
`,
|
|
Args: cobra.ExactArgs(4),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := cmd.Flags().Set(flags.FlagFrom, args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
proposalID, err := strconv.ParseUint(args[0], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
choice, err := group.ChoiceFromString(args[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := base64.StdEncoding.DecodeString(args[3])
|
|
if err != nil {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "metadata is malformed, proper base64 string is required")
|
|
}
|
|
|
|
execStr, _ := cmd.Flags().GetString(FlagExec)
|
|
|
|
msg := &group.MsgVote{
|
|
ProposalId: proposalID,
|
|
Voter: args[1],
|
|
Choice: choice,
|
|
Metadata: b,
|
|
Exec: execFromString(execStr),
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
cmd.Flags().String(FlagExec, "", "Set to 1 to try to execute proposal immediately after voting")
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// MsgExecCmd creates a CLI command for Msg/MsgExec.
|
|
func MsgExecCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "exec [proposal-id]",
|
|
Short: "Execute a proposal",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
proposalID, err := strconv.ParseUint(args[0], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := &group.MsgExec{
|
|
ProposalId: proposalID,
|
|
Signer: clientCtx.GetFromAddress().String(),
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = msg.ValidateBasic(); err != nil {
|
|
return fmt.Errorf("message validation failed: %w", err)
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|