github.com/Finschia/finschia-sdk@v0.48.1/x/gov/client/cli/query.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  	sdk "github.com/Finschia/finschia-sdk/types"
    13  	"github.com/Finschia/finschia-sdk/version"
    14  	gcutils "github.com/Finschia/finschia-sdk/x/gov/client/utils"
    15  	"github.com/Finschia/finschia-sdk/x/gov/types"
    16  )
    17  
    18  // GetQueryCmd returns the cli query commands for this module
    19  func GetQueryCmd() *cobra.Command {
    20  	// Group gov queries under a subcommand
    21  	govQueryCmd := &cobra.Command{
    22  		Use:                        types.ModuleName,
    23  		Short:                      "Querying commands for the governance module",
    24  		DisableFlagParsing:         true,
    25  		SuggestionsMinimumDistance: 2,
    26  		RunE:                       client.ValidateCmd,
    27  	}
    28  
    29  	govQueryCmd.AddCommand(
    30  		GetCmdQueryProposal(),
    31  		GetCmdQueryProposals(),
    32  		GetCmdQueryVote(),
    33  		GetCmdQueryVotes(),
    34  		GetCmdQueryParam(),
    35  		GetCmdQueryParams(),
    36  		GetCmdQueryProposer(),
    37  		GetCmdQueryDeposit(),
    38  		GetCmdQueryDeposits(),
    39  		GetCmdQueryTally(),
    40  	)
    41  
    42  	return govQueryCmd
    43  }
    44  
    45  // GetCmdQueryProposal implements the query proposal command.
    46  func GetCmdQueryProposal() *cobra.Command {
    47  	cmd := &cobra.Command{
    48  		Use:   "proposal [proposal-id]",
    49  		Args:  cobra.ExactArgs(1),
    50  		Short: "Query details of a single proposal",
    51  		Long: strings.TrimSpace(
    52  			fmt.Sprintf(`Query details for a proposal. You can find the
    53  proposal-id by running "%s query gov proposals".
    54  
    55  Example:
    56  $ %s query gov proposal 1
    57  `,
    58  				version.AppName, version.AppName,
    59  			),
    60  		),
    61  		RunE: func(cmd *cobra.Command, args []string) error {
    62  			clientCtx, err := client.GetClientQueryContext(cmd)
    63  			if err != nil {
    64  				return err
    65  			}
    66  			queryClient := types.NewQueryClient(clientCtx)
    67  
    68  			// validate that the proposal id is a uint
    69  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
    70  			if err != nil {
    71  				return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
    72  			}
    73  
    74  			// Query the proposal
    75  			res, err := queryClient.Proposal(
    76  				cmd.Context(),
    77  				&types.QueryProposalRequest{ProposalId: proposalID},
    78  			)
    79  			if err != nil {
    80  				return err
    81  			}
    82  
    83  			return clientCtx.PrintProto(&res.Proposal)
    84  		},
    85  	}
    86  
    87  	flags.AddQueryFlagsToCmd(cmd)
    88  
    89  	return cmd
    90  }
    91  
    92  // GetCmdQueryProposals implements a query proposals command. Command to Get a
    93  // Proposal Information.
    94  func GetCmdQueryProposals() *cobra.Command {
    95  	cmd := &cobra.Command{
    96  		Use:   "proposals",
    97  		Args:  cobra.NoArgs,
    98  		Short: "Query proposals with optional filters",
    99  		Long: strings.TrimSpace(
   100  			fmt.Sprintf(`Query for a all paginated proposals that match optional filters:
   101  
   102  Example:
   103  $ %s query gov proposals --depositor link1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
   104  $ %s query gov proposals --voter link1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
   105  $ %s query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Rejected)
   106  $ %s query gov proposals --page=2 --limit=100
   107  `,
   108  				version.AppName, version.AppName, version.AppName, version.AppName,
   109  			),
   110  		),
   111  		RunE: func(cmd *cobra.Command, args []string) error {
   112  			bechDepositorAddr, _ := cmd.Flags().GetString(flagDepositor)
   113  			bechVoterAddr, _ := cmd.Flags().GetString(flagVoter)
   114  			strProposalStatus, _ := cmd.Flags().GetString(flagStatus)
   115  
   116  			var proposalStatus types.ProposalStatus
   117  
   118  			if len(bechDepositorAddr) != 0 {
   119  				_, err := sdk.AccAddressFromBech32(bechDepositorAddr)
   120  				if err != nil {
   121  					return err
   122  				}
   123  			}
   124  
   125  			if len(bechVoterAddr) != 0 {
   126  				_, err := sdk.AccAddressFromBech32(bechVoterAddr)
   127  				if err != nil {
   128  					return err
   129  				}
   130  			}
   131  
   132  			if len(strProposalStatus) != 0 {
   133  				proposalStatus1, err := types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus))
   134  				proposalStatus = proposalStatus1
   135  				if err != nil {
   136  					return err
   137  				}
   138  			}
   139  
   140  			clientCtx, err := client.GetClientQueryContext(cmd)
   141  			if err != nil {
   142  				return err
   143  			}
   144  			queryClient := types.NewQueryClient(clientCtx)
   145  
   146  			pageReq, err := client.ReadPageRequest(cmd.Flags())
   147  			if err != nil {
   148  				return err
   149  			}
   150  
   151  			res, err := queryClient.Proposals(
   152  				cmd.Context(),
   153  				&types.QueryProposalsRequest{
   154  					ProposalStatus: proposalStatus,
   155  					Voter:          bechVoterAddr,
   156  					Depositor:      bechDepositorAddr,
   157  					Pagination:     pageReq,
   158  				},
   159  			)
   160  			if err != nil {
   161  				return err
   162  			}
   163  
   164  			if len(res.GetProposals()) == 0 {
   165  				return fmt.Errorf("no proposals found")
   166  			}
   167  
   168  			return clientCtx.PrintProto(res)
   169  		},
   170  	}
   171  
   172  	cmd.Flags().String(flagDepositor, "", "(optional) filter by proposals deposited on by depositor")
   173  	cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted")
   174  	cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected")
   175  	flags.AddPaginationFlagsToCmd(cmd, "proposals")
   176  	flags.AddQueryFlagsToCmd(cmd)
   177  
   178  	return cmd
   179  }
   180  
   181  // GetCmdQueryVote implements the query proposal vote command. Command to Get a
   182  // Proposal Information.
   183  func GetCmdQueryVote() *cobra.Command {
   184  	cmd := &cobra.Command{
   185  		Use:   "vote [proposal-id] [voter-addr]",
   186  		Args:  cobra.ExactArgs(2),
   187  		Short: "Query details of a single vote",
   188  		Long: strings.TrimSpace(
   189  			fmt.Sprintf(`Query details for a single vote on a proposal given its identifier.
   190  
   191  Example:
   192  $ %s query gov vote 1 link1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
   193  `,
   194  				version.AppName,
   195  			),
   196  		),
   197  		RunE: func(cmd *cobra.Command, args []string) error {
   198  			clientCtx, err := client.GetClientQueryContext(cmd)
   199  			if err != nil {
   200  				return err
   201  			}
   202  			queryClient := types.NewQueryClient(clientCtx)
   203  
   204  			// validate that the proposal id is a uint
   205  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   206  			if err != nil {
   207  				return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
   208  			}
   209  
   210  			// check to see if the proposal is in the store
   211  			ctx := cmd.Context()
   212  			_, err = queryClient.Proposal(
   213  				ctx,
   214  				&types.QueryProposalRequest{ProposalId: proposalID},
   215  			)
   216  			if err != nil {
   217  				return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
   218  			}
   219  
   220  			voterAddr, err := sdk.AccAddressFromBech32(args[1])
   221  			if err != nil {
   222  				return err
   223  			}
   224  
   225  			res, err := queryClient.Vote(
   226  				ctx,
   227  				&types.QueryVoteRequest{ProposalId: proposalID, Voter: args[1]},
   228  			)
   229  			if err != nil {
   230  				return err
   231  			}
   232  
   233  			vote := res.GetVote()
   234  			if vote.Empty() {
   235  				params := types.NewQueryVoteParams(proposalID, voterAddr)
   236  				resByTxQuery, err := gcutils.QueryVoteByTxQuery(clientCtx, params)
   237  				if err != nil {
   238  					return err
   239  				}
   240  
   241  				if err := clientCtx.Codec.UnmarshalJSON(resByTxQuery, &vote); err != nil {
   242  					return err
   243  				}
   244  			}
   245  
   246  			return clientCtx.PrintProto(&res.Vote)
   247  		},
   248  	}
   249  
   250  	flags.AddQueryFlagsToCmd(cmd)
   251  
   252  	return cmd
   253  }
   254  
   255  // GetCmdQueryVotes implements the command to query for proposal votes.
   256  func GetCmdQueryVotes() *cobra.Command {
   257  	cmd := &cobra.Command{
   258  		Use:   "votes [proposal-id]",
   259  		Args:  cobra.ExactArgs(1),
   260  		Short: "Query votes on a proposal",
   261  		Long: strings.TrimSpace(
   262  			fmt.Sprintf(`Query vote details for a single proposal by its identifier.
   263  
   264  Example:
   265  $ %[1]s query gov votes 1
   266  $ %[1]s query gov votes 1 --page=2 --limit=100
   267  `,
   268  				version.AppName,
   269  			),
   270  		),
   271  		RunE: func(cmd *cobra.Command, args []string) error {
   272  			clientCtx, err := client.GetClientQueryContext(cmd)
   273  			if err != nil {
   274  				return err
   275  			}
   276  			queryClient := types.NewQueryClient(clientCtx)
   277  
   278  			// validate that the proposal id is a uint
   279  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   280  			if err != nil {
   281  				return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
   282  			}
   283  
   284  			// check to see if the proposal is in the store
   285  			ctx := cmd.Context()
   286  			proposalRes, err := queryClient.Proposal(
   287  				ctx,
   288  				&types.QueryProposalRequest{ProposalId: proposalID},
   289  			)
   290  			if err != nil {
   291  				return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
   292  			}
   293  
   294  			propStatus := proposalRes.GetProposal().Status
   295  			if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
   296  				page, _ := cmd.Flags().GetInt(flags.FlagPage)
   297  				limit, _ := cmd.Flags().GetInt(flags.FlagLimit)
   298  
   299  				params := types.NewQueryProposalVotesParams(proposalID, page, limit)
   300  				resByTxQuery, err := gcutils.QueryVotesByTxQuery(clientCtx, params)
   301  				if err != nil {
   302  					return err
   303  				}
   304  
   305  				var votes types.Votes
   306  				// TODO migrate to use JSONCodec (implement MarshalJSONArray
   307  				// or wrap lists of proto.Message in some other message)
   308  				clientCtx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &votes)
   309  				return clientCtx.PrintObjectLegacy(votes)
   310  
   311  			}
   312  
   313  			pageReq, err := client.ReadPageRequest(cmd.Flags())
   314  			if err != nil {
   315  				return err
   316  			}
   317  
   318  			res, err := queryClient.Votes(
   319  				ctx,
   320  				&types.QueryVotesRequest{ProposalId: proposalID, Pagination: pageReq},
   321  			)
   322  			if err != nil {
   323  				return err
   324  			}
   325  
   326  			return clientCtx.PrintProto(res)
   327  		},
   328  	}
   329  
   330  	flags.AddPaginationFlagsToCmd(cmd, "votes")
   331  	flags.AddQueryFlagsToCmd(cmd)
   332  
   333  	return cmd
   334  }
   335  
   336  // GetCmdQueryDeposit implements the query proposal deposit command. Command to
   337  // get a specific Deposit Information
   338  func GetCmdQueryDeposit() *cobra.Command {
   339  	cmd := &cobra.Command{
   340  		Use:   "deposit [proposal-id] [depositer-addr]",
   341  		Args:  cobra.ExactArgs(2),
   342  		Short: "Query details of a deposit",
   343  		Long: strings.TrimSpace(
   344  			fmt.Sprintf(`Query details for a single proposal deposit on a proposal by its identifier.
   345  
   346  Example:
   347  $ %s query gov deposit 1 link1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
   348  `,
   349  				version.AppName,
   350  			),
   351  		),
   352  		RunE: func(cmd *cobra.Command, args []string) error {
   353  			clientCtx, err := client.GetClientQueryContext(cmd)
   354  			if err != nil {
   355  				return err
   356  			}
   357  			queryClient := types.NewQueryClient(clientCtx)
   358  
   359  			// validate that the proposal id is a uint
   360  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   361  			if err != nil {
   362  				return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
   363  			}
   364  
   365  			// check to see if the proposal is in the store
   366  			ctx := cmd.Context()
   367  			proposalRes, err := queryClient.Proposal(
   368  				ctx,
   369  				&types.QueryProposalRequest{ProposalId: proposalID},
   370  			)
   371  			if err != nil {
   372  				return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
   373  			}
   374  
   375  			depositorAddr, err := sdk.AccAddressFromBech32(args[1])
   376  			if err != nil {
   377  				return err
   378  			}
   379  
   380  			var deposit types.Deposit
   381  			propStatus := proposalRes.Proposal.Status
   382  			if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
   383  				params := types.NewQueryDepositParams(proposalID, depositorAddr)
   384  				resByTxQuery, err := gcutils.QueryDepositByTxQuery(clientCtx, params)
   385  				if err != nil {
   386  					return err
   387  				}
   388  				clientCtx.Codec.MustUnmarshalJSON(resByTxQuery, &deposit)
   389  				return clientCtx.PrintProto(&deposit)
   390  			}
   391  
   392  			res, err := queryClient.Deposit(
   393  				ctx,
   394  				&types.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]},
   395  			)
   396  			if err != nil {
   397  				return err
   398  			}
   399  
   400  			return clientCtx.PrintProto(&res.Deposit)
   401  		},
   402  	}
   403  
   404  	flags.AddQueryFlagsToCmd(cmd)
   405  
   406  	return cmd
   407  }
   408  
   409  // GetCmdQueryDeposits implements the command to query for proposal deposits.
   410  func GetCmdQueryDeposits() *cobra.Command {
   411  	cmd := &cobra.Command{
   412  		Use:   "deposits [proposal-id]",
   413  		Args:  cobra.ExactArgs(1),
   414  		Short: "Query deposits on a proposal",
   415  		Long: strings.TrimSpace(
   416  			fmt.Sprintf(`Query details for all deposits on a proposal.
   417  You can find the proposal-id by running "%s query gov proposals".
   418  
   419  Example:
   420  $ %s query gov deposits 1
   421  `,
   422  				version.AppName, version.AppName,
   423  			),
   424  		),
   425  		RunE: func(cmd *cobra.Command, args []string) error {
   426  			clientCtx, err := client.GetClientQueryContext(cmd)
   427  			if err != nil {
   428  				return err
   429  			}
   430  			queryClient := types.NewQueryClient(clientCtx)
   431  
   432  			// validate that the proposal id is a uint
   433  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   434  			if err != nil {
   435  				return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
   436  			}
   437  
   438  			// check to see if the proposal is in the store
   439  			ctx := cmd.Context()
   440  			proposalRes, err := queryClient.Proposal(
   441  				ctx,
   442  				&types.QueryProposalRequest{ProposalId: proposalID},
   443  			)
   444  			if err != nil {
   445  				return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
   446  			}
   447  
   448  			propStatus := proposalRes.GetProposal().Status
   449  			if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
   450  				params := types.NewQueryProposalParams(proposalID)
   451  				resByTxQuery, err := gcutils.QueryDepositsByTxQuery(clientCtx, params)
   452  				if err != nil {
   453  					return err
   454  				}
   455  
   456  				var dep types.Deposits
   457  				// TODO migrate to use JSONCodec (implement MarshalJSONArray
   458  				// or wrap lists of proto.Message in some other message)
   459  				clientCtx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &dep)
   460  
   461  				return clientCtx.PrintObjectLegacy(dep)
   462  			}
   463  
   464  			pageReq, err := client.ReadPageRequest(cmd.Flags())
   465  			if err != nil {
   466  				return err
   467  			}
   468  
   469  			res, err := queryClient.Deposits(
   470  				ctx,
   471  				&types.QueryDepositsRequest{ProposalId: proposalID, Pagination: pageReq},
   472  			)
   473  			if err != nil {
   474  				return err
   475  			}
   476  
   477  			return clientCtx.PrintProto(res)
   478  		},
   479  	}
   480  
   481  	flags.AddPaginationFlagsToCmd(cmd, "deposits")
   482  	flags.AddQueryFlagsToCmd(cmd)
   483  
   484  	return cmd
   485  }
   486  
   487  // GetCmdQueryTally implements the command to query for proposal tally result.
   488  func GetCmdQueryTally() *cobra.Command {
   489  	cmd := &cobra.Command{
   490  		Use:   "tally [proposal-id]",
   491  		Args:  cobra.ExactArgs(1),
   492  		Short: "Get the tally of a proposal vote",
   493  		Long: strings.TrimSpace(
   494  			fmt.Sprintf(`Query tally of votes on a proposal. You can find
   495  the proposal-id by running "%s query gov proposals".
   496  
   497  Example:
   498  $ %s query gov tally 1
   499  `,
   500  				version.AppName, version.AppName,
   501  			),
   502  		),
   503  		RunE: func(cmd *cobra.Command, args []string) error {
   504  			clientCtx, err := client.GetClientQueryContext(cmd)
   505  			if err != nil {
   506  				return err
   507  			}
   508  			queryClient := types.NewQueryClient(clientCtx)
   509  
   510  			// validate that the proposal id is a uint
   511  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   512  			if err != nil {
   513  				return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
   514  			}
   515  
   516  			// check to see if the proposal is in the store
   517  			ctx := cmd.Context()
   518  			_, err = queryClient.Proposal(
   519  				ctx,
   520  				&types.QueryProposalRequest{ProposalId: proposalID},
   521  			)
   522  			if err != nil {
   523  				return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
   524  			}
   525  
   526  			// Query store
   527  			res, err := queryClient.TallyResult(
   528  				ctx,
   529  				&types.QueryTallyResultRequest{ProposalId: proposalID},
   530  			)
   531  			if err != nil {
   532  				return err
   533  			}
   534  
   535  			return clientCtx.PrintProto(&res.Tally)
   536  		},
   537  	}
   538  
   539  	flags.AddQueryFlagsToCmd(cmd)
   540  
   541  	return cmd
   542  }
   543  
   544  // GetCmdQueryParams implements the query params command.
   545  func GetCmdQueryParams() *cobra.Command {
   546  	cmd := &cobra.Command{
   547  		Use:   "params",
   548  		Short: "Query the parameters of the governance process",
   549  		Long: strings.TrimSpace(
   550  			fmt.Sprintf(`Query the all the parameters for the governance process.
   551  
   552  Example:
   553  $ %s query gov params
   554  `,
   555  				version.AppName,
   556  			),
   557  		),
   558  		Args: cobra.NoArgs,
   559  		RunE: func(cmd *cobra.Command, args []string) error {
   560  			clientCtx, err := client.GetClientQueryContext(cmd)
   561  			if err != nil {
   562  				return err
   563  			}
   564  			queryClient := types.NewQueryClient(clientCtx)
   565  
   566  			// Query store for all 3 params
   567  			ctx := cmd.Context()
   568  			votingRes, err := queryClient.Params(
   569  				ctx,
   570  				&types.QueryParamsRequest{ParamsType: "voting"},
   571  			)
   572  			if err != nil {
   573  				return err
   574  			}
   575  
   576  			tallyRes, err := queryClient.Params(
   577  				ctx,
   578  				&types.QueryParamsRequest{ParamsType: "tallying"},
   579  			)
   580  			if err != nil {
   581  				return err
   582  			}
   583  
   584  			depositRes, err := queryClient.Params(
   585  				ctx,
   586  				&types.QueryParamsRequest{ParamsType: "deposit"},
   587  			)
   588  			if err != nil {
   589  				return err
   590  			}
   591  
   592  			params := types.NewParams(
   593  				votingRes.GetVotingParams(),
   594  				tallyRes.GetTallyParams(),
   595  				depositRes.GetDepositParams(),
   596  			)
   597  
   598  			return clientCtx.PrintObjectLegacy(params)
   599  		},
   600  	}
   601  
   602  	flags.AddQueryFlagsToCmd(cmd)
   603  
   604  	return cmd
   605  }
   606  
   607  // GetCmdQueryParam implements the query param command.
   608  func GetCmdQueryParam() *cobra.Command {
   609  	cmd := &cobra.Command{
   610  		Use:   "param [param-type]",
   611  		Args:  cobra.ExactArgs(1),
   612  		Short: "Query the parameters (voting|tallying|deposit) of the governance process",
   613  		Long: strings.TrimSpace(
   614  			fmt.Sprintf(`Query the all the parameters for the governance process.
   615  
   616  Example:
   617  $ %s query gov param voting
   618  $ %s query gov param tallying
   619  $ %s query gov param deposit
   620  `,
   621  				version.AppName, version.AppName, version.AppName,
   622  			),
   623  		),
   624  		RunE: func(cmd *cobra.Command, args []string) error {
   625  			clientCtx, err := client.GetClientQueryContext(cmd)
   626  			if err != nil {
   627  				return err
   628  			}
   629  			queryClient := types.NewQueryClient(clientCtx)
   630  
   631  			// Query store
   632  			res, err := queryClient.Params(
   633  				cmd.Context(),
   634  				&types.QueryParamsRequest{ParamsType: args[0]},
   635  			)
   636  			if err != nil {
   637  				return err
   638  			}
   639  
   640  			var out fmt.Stringer
   641  			switch args[0] {
   642  			case "voting":
   643  				out = res.GetVotingParams()
   644  			case "tallying":
   645  				out = res.GetTallyParams()
   646  			case "deposit":
   647  				out = res.GetDepositParams()
   648  			default:
   649  				return fmt.Errorf("argument must be one of (voting|tallying|deposit), was %s", args[0])
   650  			}
   651  
   652  			return clientCtx.PrintObjectLegacy(out)
   653  		},
   654  	}
   655  
   656  	flags.AddQueryFlagsToCmd(cmd)
   657  
   658  	return cmd
   659  }
   660  
   661  // GetCmdQueryProposer implements the query proposer command.
   662  func GetCmdQueryProposer() *cobra.Command {
   663  	cmd := &cobra.Command{
   664  		Use:   "proposer [proposal-id]",
   665  		Args:  cobra.ExactArgs(1),
   666  		Short: "Query the proposer of a governance proposal",
   667  		Long: strings.TrimSpace(
   668  			fmt.Sprintf(`Query which address proposed a proposal with a given ID.
   669  
   670  Example:
   671  $ %s query gov proposer 1
   672  `,
   673  				version.AppName,
   674  			),
   675  		),
   676  		RunE: func(cmd *cobra.Command, args []string) error {
   677  			clientCtx, err := client.GetClientQueryContext(cmd)
   678  			if err != nil {
   679  				return err
   680  			}
   681  
   682  			// validate that the proposalID is a uint
   683  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   684  			if err != nil {
   685  				return fmt.Errorf("proposal-id %s is not a valid uint", args[0])
   686  			}
   687  
   688  			prop, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID)
   689  			if err != nil {
   690  				return err
   691  			}
   692  
   693  			return clientCtx.PrintObjectLegacy(prop)
   694  		},
   695  	}
   696  
   697  	flags.AddQueryFlagsToCmd(cmd)
   698  
   699  	return cmd
   700  }