package cli import ( "fmt" "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" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/gov" "encoding/json" "io/ioutil" "strings" govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( flagProposalID = "proposal-id" flagTitle = "title" flagDescription = "description" flagProposalType = "type" flagDeposit = "deposit" flagVoter = "voter" flagOption = "option" flagDepositer = "depositer" flagStatus = "status" flagNumLimit = "limit" flagProposal = "proposal" ) type proposal struct { Title string Description string Type string Deposit string } 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(` Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example: $ gaiacli gov submit-proposal --proposal="path/to/proposal.json" where proposal.json contains: { "title": "Test Proposal", "description": "My awesome proposal", "type": "Text", "deposit": "1000test" } is equivalent to $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test" `), RunE: func(cmd *cobra.Command, args []string) error { proposal, err := parseSubmitProposalFlags() if err != nil { return err } txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) fromAddr, err := cliCtx.GetFromAddress() if err != nil { return err } amount, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } proposalType, err := gov.ProposalTypeFromString(proposal.Type) if err != nil { return err } msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount) err = msg.ValidateBasic() if err != nil { return err } if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to Tendermint // proposalID must be returned, and it is a part of response. cliCtx.PrintResponse = true return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []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 } func parseSubmitProposalFlags() (*proposal, error) { proposal := &proposal{} proposalFile := viper.GetString(flagProposal) if proposalFile == "" { proposal.Title = viper.GetString(flagTitle) proposal.Description = viper.GetString(flagDescription) proposal.Type = govClientUtils.NormalizeProposalType(viper.GetString(flagProposalType)) proposal.Deposit = viper.GetString(flagDeposit) return proposal, nil } for _, flag := range proposalFlags { if viper.GetString(flag) != "" { return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) } } contents, err := ioutil.ReadFile(proposalFile) if err != nil { return nil, err } err = json.Unmarshal(contents, proposal) if err != nil { return nil, err } return proposal, nil } // GetCmdDeposit implements depositing tokens for an active proposal. func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "deposit", Short: "Deposit tokens for activing proposal", RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) depositerAddr, err := cliCtx.GetFromAddress() if err != nil { return err } proposalID := uint64(viper.GetInt64(flagProposalID)) amount, err := sdk.ParseCoins(viper.GetString(flagDeposit)) if err != nil { return err } msg := gov.NewMsgDeposit(depositerAddr, proposalID, amount) err = msg.ValidateBasic() if err != nil { return err } if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to a Tendermint // node. return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, } cmd.Flags().String(flagProposalID, "", "proposalID of proposal depositing on") cmd.Flags().String(flagDeposit, "", "amount of deposit") return cmd } // GetCmdVote implements creating a new vote command. func GetCmdVote(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "vote", Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", RunE: func(cmd *cobra.Command, args []string) error { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). WithAccountDecoder(cdc) voterAddr, err := cliCtx.GetFromAddress() if err != nil { return err } proposalID := uint64(viper.GetInt64(flagProposalID)) option := viper.GetString(flagOption) byteVoteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(option)) if err != nil { return err } msg := gov.NewMsgVote(voterAddr, proposalID, byteVoteOption) err = msg.ValidateBasic() if err != nil { return err } if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) } fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]", voterAddr.String(), msg.ProposalID, msg.Option.String(), ) // Build and sign the transaction, then broadcast to a Tendermint // node. return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, } cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on") cmd.Flags().String(flagOption, "", "vote option {yes, no, no_with_veto, abstain}") return cmd }