github.com/Finschia/finschia-sdk@v0.48.1/x/gov/client/cli/tx.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/spf13/cobra"
     9  
    10  	"github.com/Finschia/finschia-sdk/client"
    11  	"github.com/Finschia/finschia-sdk/client/flags"
    12  	"github.com/Finschia/finschia-sdk/client/tx"
    13  	sdk "github.com/Finschia/finschia-sdk/types"
    14  	"github.com/Finschia/finschia-sdk/version"
    15  	govutils "github.com/Finschia/finschia-sdk/x/gov/client/utils"
    16  	"github.com/Finschia/finschia-sdk/x/gov/types"
    17  )
    18  
    19  // Proposal flags
    20  const (
    21  	FlagTitle        = "title"
    22  	FlagDescription  = "description"
    23  	FlagProposalType = "type"
    24  	FlagDeposit      = "deposit"
    25  	flagVoter        = "voter"
    26  	flagDepositor    = "depositor"
    27  	flagStatus       = "status"
    28  	FlagProposal     = "proposal"
    29  )
    30  
    31  type proposal struct {
    32  	Title       string
    33  	Description string
    34  	Type        string
    35  	Deposit     string
    36  }
    37  
    38  // ProposalFlags defines the core required fields of a proposal. It is used to
    39  // verify that these values are not provided in conjunction with a JSON proposal
    40  // file.
    41  var ProposalFlags = []string{
    42  	FlagTitle,
    43  	FlagDescription,
    44  	FlagProposalType,
    45  	FlagDeposit,
    46  }
    47  
    48  // NewTxCmd returns the transaction commands for this module
    49  // governance ModuleClient is slightly different from other ModuleClients in that
    50  // it contains a slice of "proposal" child commands. These commands are respective
    51  // to proposal type handlers that are implemented in other modules but are mounted
    52  // under the governance CLI (eg. parameter change proposals).
    53  func NewTxCmd(propCmds []*cobra.Command) *cobra.Command {
    54  	govTxCmd := &cobra.Command{
    55  		Use:                        types.ModuleName,
    56  		Short:                      "Governance transactions subcommands",
    57  		DisableFlagParsing:         true,
    58  		SuggestionsMinimumDistance: 2,
    59  		RunE:                       client.ValidateCmd,
    60  	}
    61  
    62  	cmdSubmitProp := NewCmdSubmitProposal()
    63  	for _, propCmd := range propCmds {
    64  		flags.AddTxFlagsToCmd(propCmd)
    65  		cmdSubmitProp.AddCommand(propCmd)
    66  	}
    67  
    68  	govTxCmd.AddCommand(
    69  		NewCmdDeposit(),
    70  		NewCmdVote(),
    71  		NewCmdWeightedVote(),
    72  		cmdSubmitProp,
    73  	)
    74  
    75  	return govTxCmd
    76  }
    77  
    78  // NewCmdSubmitProposal implements submitting a proposal transaction command.
    79  func NewCmdSubmitProposal() *cobra.Command {
    80  	cmd := &cobra.Command{
    81  		Use:   "submit-proposal",
    82  		Short: "Submit a proposal along with an initial deposit",
    83  		Long: strings.TrimSpace(
    84  			fmt.Sprintf(`Submit a proposal along with an initial deposit.
    85  Proposal title, description, type and deposit can be given directly or through a proposal JSON file.
    86  
    87  Example:
    88  $ %s tx gov submit-proposal --proposal="path/to/proposal.json" --from mykey
    89  
    90  Where proposal.json contains:
    91  
    92  {
    93    "title": "Test Proposal",
    94    "description": "My awesome proposal",
    95    "type": "Text",
    96    "deposit": "10test"
    97  }
    98  
    99  Which is equivalent to:
   100  
   101  $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="10test" --from mykey
   102  `,
   103  				version.AppName, version.AppName,
   104  			),
   105  		),
   106  		RunE: func(cmd *cobra.Command, args []string) error {
   107  			clientCtx, err := client.GetClientTxContext(cmd)
   108  			if err != nil {
   109  				return err
   110  			}
   111  
   112  			proposal, err := parseSubmitProposalFlags(cmd.Flags())
   113  			if err != nil {
   114  				return fmt.Errorf("failed to parse proposal: %w", err)
   115  			}
   116  
   117  			amount, err := sdk.ParseCoinsNormalized(proposal.Deposit)
   118  			if err != nil {
   119  				return err
   120  			}
   121  
   122  			content := types.ContentFromProposalType(proposal.Title, proposal.Description, proposal.Type)
   123  
   124  			msg, err := types.NewMsgSubmitProposal(content, amount, clientCtx.GetFromAddress())
   125  			if err != nil {
   126  				return fmt.Errorf("invalid message: %w", err)
   127  			}
   128  
   129  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   130  		},
   131  	}
   132  
   133  	cmd.Flags().String(FlagTitle, "", "The proposal title")
   134  	cmd.Flags().String(FlagDescription, "", "The proposal description")
   135  	cmd.Flags().String(FlagProposalType, "", "The proposal Type")
   136  	cmd.Flags().String(FlagDeposit, "", "The proposal deposit")
   137  	cmd.Flags().String(FlagProposal, "", "Proposal file path (if this path is given, other proposal flags are ignored)")
   138  	flags.AddTxFlagsToCmd(cmd)
   139  
   140  	return cmd
   141  }
   142  
   143  // NewCmdDeposit implements depositing tokens for an active proposal.
   144  func NewCmdDeposit() *cobra.Command {
   145  	cmd := &cobra.Command{
   146  		Use:   "deposit [proposal-id] [deposit]",
   147  		Args:  cobra.ExactArgs(2),
   148  		Short: "Deposit tokens for an active proposal",
   149  		Long: strings.TrimSpace(
   150  			fmt.Sprintf(`Submit a deposit for an active proposal. You can
   151  find the proposal-id by running "%s query gov proposals".
   152  
   153  Example:
   154  $ %s tx gov deposit 1 10stake --from mykey
   155  `,
   156  				version.AppName, version.AppName,
   157  			),
   158  		),
   159  		RunE: func(cmd *cobra.Command, args []string) error {
   160  			clientCtx, err := client.GetClientTxContext(cmd)
   161  			if err != nil {
   162  				return err
   163  			}
   164  
   165  			// validate that the proposal id is a uint
   166  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   167  			if err != nil {
   168  				return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
   169  			}
   170  
   171  			// Get depositor address
   172  			from := clientCtx.GetFromAddress()
   173  
   174  			// Get amount of coins
   175  			amount, err := sdk.ParseCoinsNormalized(args[1])
   176  			if err != nil {
   177  				return err
   178  			}
   179  
   180  			msg := types.NewMsgDeposit(from, proposalID, amount)
   181  
   182  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   183  		},
   184  	}
   185  
   186  	flags.AddTxFlagsToCmd(cmd)
   187  
   188  	return cmd
   189  }
   190  
   191  // NewCmdVote implements creating a new vote command.
   192  func NewCmdVote() *cobra.Command {
   193  	cmd := &cobra.Command{
   194  		Use:   "vote [proposal-id] [option]",
   195  		Args:  cobra.ExactArgs(2),
   196  		Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain",
   197  		Long: strings.TrimSpace(
   198  			fmt.Sprintf(`Submit a vote for an active proposal. You can
   199  find the proposal-id by running "%s query gov proposals".
   200  
   201  Example:
   202  $ %s tx gov vote 1 yes --from mykey
   203  `,
   204  				version.AppName, version.AppName,
   205  			),
   206  		),
   207  		RunE: func(cmd *cobra.Command, args []string) error {
   208  			clientCtx, err := client.GetClientTxContext(cmd)
   209  			if err != nil {
   210  				return err
   211  			}
   212  			// Get voting address
   213  			from := clientCtx.GetFromAddress()
   214  
   215  			// validate that the proposal id is a uint
   216  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   217  			if err != nil {
   218  				return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
   219  			}
   220  
   221  			// Find out which vote option user chose
   222  			byteVoteOption, err := types.VoteOptionFromString(govutils.NormalizeVoteOption(args[1]))
   223  			if err != nil {
   224  				return err
   225  			}
   226  
   227  			// Build vote message and run basic validation
   228  			msg := types.NewMsgVote(from, proposalID, byteVoteOption)
   229  
   230  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   231  		},
   232  	}
   233  
   234  	flags.AddTxFlagsToCmd(cmd)
   235  
   236  	return cmd
   237  }
   238  
   239  // NewCmdWeightedVote implements creating a new weighted vote command.
   240  func NewCmdWeightedVote() *cobra.Command {
   241  	cmd := &cobra.Command{
   242  		Use:   "weighted-vote [proposal-id] [weighted-options]",
   243  		Args:  cobra.ExactArgs(2),
   244  		Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain",
   245  		Long: strings.TrimSpace(
   246  			fmt.Sprintf(`Submit a vote for an active proposal. You can
   247  find the proposal-id by running "%s query gov proposals".
   248  
   249  Example:
   250  $ %s tx gov weighted-vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from mykey
   251  `,
   252  				version.AppName, version.AppName,
   253  			),
   254  		),
   255  		RunE: func(cmd *cobra.Command, args []string) error {
   256  			clientCtx, err := client.GetClientTxContext(cmd)
   257  			if err != nil {
   258  				return err
   259  			}
   260  
   261  			// Get voter address
   262  			from := clientCtx.GetFromAddress()
   263  
   264  			// validate that the proposal id is a uint
   265  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   266  			if err != nil {
   267  				return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
   268  			}
   269  
   270  			// Figure out which vote options user chose
   271  			options, err := types.WeightedVoteOptionsFromString(govutils.NormalizeWeightedVoteOptions(args[1]))
   272  			if err != nil {
   273  				return err
   274  			}
   275  
   276  			// Build vote message and run basic validation
   277  			msg := types.NewMsgVoteWeighted(from, proposalID, options)
   278  			err = msg.ValidateBasic()
   279  			if err != nil {
   280  				return err
   281  			}
   282  
   283  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   284  		},
   285  	}
   286  
   287  	flags.AddTxFlagsToCmd(cmd)
   288  
   289  	return cmd
   290  }