package cli import ( "fmt" "strconv" "strings" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) // GetQueryCmd returns the cli query commands for this module func GetQueryCmd() *cobra.Command { // Group gov queries under a subcommand govQueryCmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the governance module", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } govQueryCmd.AddCommand( GetCmdQueryProposal(), GetCmdQueryProposals(), GetCmdQueryVote(), GetCmdQueryVotes(), GetCmdQueryParams(), GetCmdQueryParam(), GetCmdQueryProposer(), GetCmdQueryDeposit(), GetCmdQueryDeposits(), GetCmdQueryTally(), ) return govQueryCmd } // GetCmdQueryProposal implements the query proposal command. func GetCmdQueryProposal() *cobra.Command { cmd := &cobra.Command{ Use: "proposal [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query details of a single proposal", Long: strings.TrimSpace( fmt.Sprintf(`Query details for a proposal. You can find the proposal-id by running "%s query gov proposals". Example: $ %s query gov proposal 1 `, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // Query the proposal res, err := queryClient.Proposal( cmd.Context(), &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return err } return clientCtx.PrintProto(res.Proposal) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryProposals implements a query proposals command. Command to Get // Proposals Information. func GetCmdQueryProposals() *cobra.Command { cmd := &cobra.Command{ Use: "proposals", Short: "Query proposals with optional filters", Long: strings.TrimSpace( fmt.Sprintf(`Query for a all paginated proposals that match optional filters: Example: $ %s query gov proposals --depositor cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk $ %s query gov proposals --voter cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk $ %s query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Rejected) $ %s query gov proposals --page=2 --limit=100 `, version.AppName, version.AppName, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { bechDepositorAddr, _ := cmd.Flags().GetString(flagDepositor) bechVoterAddr, _ := cmd.Flags().GetString(flagVoter) strProposalStatus, _ := cmd.Flags().GetString(flagStatus) var proposalStatus v1.ProposalStatus if len(bechDepositorAddr) != 0 { _, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { return err } } if len(bechVoterAddr) != 0 { _, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { return err } } if len(strProposalStatus) != 0 { proposalStatus1, err := v1.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus)) proposalStatus = proposalStatus1 if err != nil { return err } } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } res, err := queryClient.Proposals( cmd.Context(), &v1.QueryProposalsRequest{ ProposalStatus: proposalStatus, Voter: bechVoterAddr, Depositor: bechDepositorAddr, Pagination: pageReq, }, ) if err != nil { return err } if len(res.GetProposals()) == 0 { return fmt.Errorf("no proposals found") } return clientCtx.PrintProto(res) }, } cmd.Flags().String(flagDepositor, "", "(optional) filter by proposals deposited on by depositor") cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") flags.AddPaginationFlagsToCmd(cmd, "proposals") flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryVote implements the query proposal vote command. Command to Get a // Vote Information. func GetCmdQueryVote() *cobra.Command { cmd := &cobra.Command{ Use: "vote [proposal-id] [voter-addr]", Args: cobra.ExactArgs(2), Short: "Query details of a single vote", Long: strings.TrimSpace( fmt.Sprintf(`Query details for a single vote on a proposal given its identifier. Example: $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // check to see if the proposal is in the store ctx := cmd.Context() _, err = queryClient.Proposal( ctx, &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } voterAddr, err := sdk.AccAddressFromBech32(args[1]) if err != nil { return err } res, err := queryClient.Vote( ctx, &v1.QueryVoteRequest{ProposalId: proposalID, Voter: args[1]}, ) if err != nil { return err } vote := res.GetVote() if vote.Empty() { params := v1.NewQueryVoteParams(proposalID, voterAddr) resByTxQuery, err := gcutils.QueryVoteByTxQuery(clientCtx, params) if err != nil { return err } if err := clientCtx.Codec.UnmarshalJSON(resByTxQuery, vote); err != nil { return err } } return clientCtx.PrintProto(res.Vote) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryVotes implements the command to query for proposal votes. func GetCmdQueryVotes() *cobra.Command { cmd := &cobra.Command{ Use: "votes [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query votes on a proposal", Long: strings.TrimSpace( fmt.Sprintf(`Query vote details for a single proposal by its identifier. Example: $ %[1]s query gov votes 1 $ %[1]s query gov votes 1 --page=2 --limit=100 `, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // check to see if the proposal is in the store ctx := cmd.Context() proposalRes, err := queryClient.Proposal( ctx, &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } propStatus := proposalRes.GetProposal().Status if !(propStatus == v1.StatusVotingPeriod || propStatus == v1.StatusDepositPeriod) { page, _ := cmd.Flags().GetInt(flags.FlagPage) limit, _ := cmd.Flags().GetInt(flags.FlagLimit) params := v1.NewQueryProposalVotesParams(proposalID, page, limit) resByTxQuery, err := gcutils.QueryVotesByTxQuery(clientCtx, params) if err != nil { return err } var votes v1.Votes // TODO migrate to use JSONCodec (implement MarshalJSONArray // or wrap lists of proto.Message in some other message) clientCtx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &votes) return clientCtx.PrintObjectLegacy(votes) } pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } res, err := queryClient.Votes( ctx, &v1.QueryVotesRequest{ProposalId: proposalID, Pagination: pageReq}, ) if err != nil { return err } return clientCtx.PrintProto(res) }, } flags.AddPaginationFlagsToCmd(cmd, "votes") flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryDeposit implements the query proposal deposit command. Command to // get a specific Deposit Information. func GetCmdQueryDeposit() *cobra.Command { cmd := &cobra.Command{ Use: "deposit [proposal-id] [depositer-addr]", Args: cobra.ExactArgs(2), Short: "Query details of a deposit", Long: strings.TrimSpace( fmt.Sprintf(`Query details for a single proposal deposit on a proposal by its identifier. Example: $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // check to see if the proposal is in the store ctx := cmd.Context() _, err = queryClient.Proposal( ctx, &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } res, err := queryClient.Deposit( ctx, &v1.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, ) if err != nil { return err } return clientCtx.PrintProto(res.Deposit) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryDeposits implements the command to query for proposal deposits. func GetCmdQueryDeposits() *cobra.Command { cmd := &cobra.Command{ Use: "deposits [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query deposits on a proposal", Long: strings.TrimSpace( fmt.Sprintf(`Query details for all deposits on a proposal. You can find the proposal-id by running "%s query gov proposals". Example: $ %s query gov deposits 1 `, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // check to see if the proposal is in the store ctx := cmd.Context() _, err = queryClient.Proposal( ctx, &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } res, err := queryClient.Deposits( ctx, &v1.QueryDepositsRequest{ProposalId: proposalID, Pagination: pageReq}, ) if err != nil { return err } return clientCtx.PrintProto(res) }, } flags.AddPaginationFlagsToCmd(cmd, "deposits") flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryTally implements the command to query for proposal tally result. func GetCmdQueryTally() *cobra.Command { cmd := &cobra.Command{ Use: "tally [proposal-id]", Args: cobra.ExactArgs(1), Short: "Get the tally of a proposal vote", Long: strings.TrimSpace( fmt.Sprintf(`Query tally of votes on a proposal. You can find the proposal-id by running "%s query gov proposals". Example: $ %s query gov tally 1 `, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // 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]) } // check to see if the proposal is in the store ctx := cmd.Context() _, err = queryClient.Proposal( ctx, &v1.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } // Query store res, err := queryClient.TallyResult( ctx, &v1.QueryTallyResultRequest{ProposalId: proposalID}, ) if err != nil { return err } return clientCtx.PrintProto(res.Tally) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryParams implements the query params command. // //nolint:staticcheck // this function contains deprecated commands that we need. func GetCmdQueryParams() *cobra.Command { cmd := &cobra.Command{ Use: "params", Short: "Query the parameters of the governance process", Long: strings.TrimSpace( fmt.Sprintf(`Query the all the parameters for the governance process. Example: $ %s query gov params `, version.AppName, ), ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // Query store for all 3 params ctx := cmd.Context() res, err := queryClient.Params( ctx, &v1.QueryParamsRequest{ParamsType: "deposit"}, ) if err != nil { return err } vp := v1.NewVotingParams(res.Params.VotingPeriod) res.VotingParams = &vp tp := v1.NewTallyParams(res.Params.Quorum, res.Params.Threshold, res.Params.VetoThreshold) res.TallyParams = &tp return clientCtx.PrintProto(res) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryParam implements the query param command. func GetCmdQueryParam() *cobra.Command { cmd := &cobra.Command{ Use: "param [param-type]", Args: cobra.ExactArgs(1), Short: "Query the parameters (voting|tallying|deposit) of the governance process", Long: strings.TrimSpace( fmt.Sprintf(`Query the all the parameters for the governance process. Example: $ %s query gov param voting $ %s query gov param tallying $ %s query gov param deposit `, version.AppName, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } queryClient := v1.NewQueryClient(clientCtx) // Query store res, err := queryClient.Params( cmd.Context(), &v1.QueryParamsRequest{ParamsType: args[0]}, ) if err != nil { return err } var out fmt.Stringer //nolint:staticcheck // this switch statement contains deprecated commands that we need. switch args[0] { case "voting": out = res.GetVotingParams() case "tallying": out = res.GetTallyParams() case "deposit": out = res.GetDepositParams() default: return fmt.Errorf("argument must be one of (voting|tallying|deposit), was %s", args[0]) } return clientCtx.PrintObjectLegacy(out) }, } flags.AddQueryFlagsToCmd(cmd) return cmd } // GetCmdQueryProposer implements the query proposer command. func GetCmdQueryProposer() *cobra.Command { cmd := &cobra.Command{ Use: "proposer [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query the proposer of a governance proposal", Long: strings.TrimSpace( fmt.Sprintf(`Query which address proposed a proposal with a given ID. Example: $ %s query gov proposer 1 `, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } // validate that the proposalID is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return fmt.Errorf("proposal-id %s is not a valid uint", args[0]) } prop, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID) if err != nil { return err } return clientCtx.PrintObjectLegacy(prop) }, } flags.AddQueryFlagsToCmd(cmd) return cmd }