package cli import ( "fmt" "strconv" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/gov" "strings" "github.com/spf13/cobra" govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" ) // Proposal flags const ( FlagTitle = "title" FlagDescription = "description" flagProposalType = "type" FlagDeposit = "deposit" flagVoter = "voter" flagDepositor = "depositor" flagStatus = "status" flagNumLimit = "limit" FlagProposal = "proposal" ) type proposal struct { Title string Description string Type string Deposit string } // ProposalFlags defines the core required fields of a proposal. It is used to // verify that these values are not provided in conjunction with a JSON proposal // file. var ProposalFlags = []string{ FlagTitle, FlagDescription, flagProposalType, FlagDeposit, } // GetCmdSubmitProposal implements submitting a proposal transaction command. func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "submit-proposal", Short: "Submit a proposal along with an initial deposit", Long: strings.TrimSpace( fmt.Sprintf(`Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. Example: $ %s tx gov submit-proposal --proposal="path/to/proposal.json" --from mykey Where proposal.json contains: { "title": "Test Proposal", "description": "My awesome proposal", "type": "Text", "deposit": "10test" } Which is equivalent to: $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="10test" --from mykey `, version.ClientName, version.ClientName, ), ), RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) proposal, err := parseSubmitProposalFlags() if err != nil { return err } amount, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } content := gov.ContentFromProposalType(proposal.Title, proposal.Description, proposal.Type) msg := gov.NewMsgSubmitProposal(content, amount, cliCtx.GetFromAddress()) if err := msg.ValidateBasic(); err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } cmd.Flags().String(FlagTitle, "", "title of proposal") cmd.Flags().String(FlagDescription, "", "description of proposal") cmd.Flags().String(flagProposalType, "", "proposalType of proposal, types: text/parameter_change/software_upgrade") cmd.Flags().String(FlagDeposit, "", "deposit of proposal") cmd.Flags().String(FlagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)") return cmd } // GetCmdDeposit implements depositing tokens for an active proposal. func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "deposit [proposal-id] [deposit]", Args: cobra.ExactArgs(2), Short: "Deposit tokens for an active proposal", Long: strings.TrimSpace( fmt.Sprintf(`Submit a deposit for an active proposal. You can find the proposal-id by running "%s query gov proposals". Example: $ %s tx gov deposit 1 10stake --from mykey `, version.ClientName, version.ClientName, ), ), RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) } // Get depositor address from := cliCtx.GetFromAddress() // Get amount of coins amount, err := sdk.ParseCoins(args[1]) if err != nil { return err } msg := gov.NewMsgDeposit(from, proposalID, amount) err = msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } } // GetCmdVote implements creating a new vote command. func GetCmdVote(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "vote [proposal-id] [option]", Args: cobra.ExactArgs(2), Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", Long: strings.TrimSpace( fmt.Sprintf(`Submit a vote for an active proposal. You can find the proposal-id by running "%s query gov proposals". Example: $ %s tx gov vote 1 yes --from mykey `, version.ClientName, version.ClientName, ), ), RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) // Get voting address from := cliCtx.GetFromAddress() // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) } // Find out which vote option user chose byteVoteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(args[1])) if err != nil { return err } // Build vote message and run basic validation msg := gov.NewMsgVote(from, proposalID, byteVoteOption) err = msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } } // DONTCOVER