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

     1  package cli
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     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  	"github.com/Finschia/finschia-sdk/codec"
    14  	sdk "github.com/Finschia/finschia-sdk/types"
    15  	"github.com/Finschia/finschia-sdk/x/foundation"
    16  	govcli "github.com/Finschia/finschia-sdk/x/gov/client/cli"
    17  	govtypes "github.com/Finschia/finschia-sdk/x/gov/types"
    18  )
    19  
    20  // Proposal flags
    21  const (
    22  	FlagExec = "exec"
    23  	ExecTry  = "try"
    24  )
    25  
    26  func validateGenerateOnly(cmd *cobra.Command) error {
    27  	generateOnly, err := cmd.Flags().GetBool(flags.FlagGenerateOnly)
    28  	if err != nil {
    29  		return err
    30  	}
    31  	if !generateOnly {
    32  		return fmt.Errorf("you must use it with the flag --%s", flags.FlagGenerateOnly)
    33  	}
    34  	return nil
    35  }
    36  
    37  func parseMemberRequests(codec codec.Codec, membersJSON string) ([]foundation.MemberRequest, error) {
    38  	var cliMembers []json.RawMessage
    39  	if err := json.Unmarshal([]byte(membersJSON), &cliMembers); err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	members := make([]foundation.MemberRequest, len(cliMembers))
    44  	for i, cliMember := range cliMembers {
    45  		var member foundation.MemberRequest
    46  		if err := codec.UnmarshalJSON(cliMember, &member); err != nil {
    47  			return nil, err
    48  		}
    49  		members[i] = member
    50  	}
    51  
    52  	return members, nil
    53  }
    54  
    55  func parseAddresses(addressesJSON string) ([]string, error) {
    56  	var addresses []string
    57  	if err := json.Unmarshal([]byte(addressesJSON), &addresses); err != nil {
    58  		return nil, err
    59  	}
    60  	if len(addresses) == 0 {
    61  		return nil, fmt.Errorf("you must provide one address at least")
    62  	}
    63  
    64  	return addresses, nil
    65  }
    66  
    67  func parseDecisionPolicy(codec codec.Codec, policyJSON string) (foundation.DecisionPolicy, error) {
    68  	var policy foundation.DecisionPolicy
    69  	if err := codec.UnmarshalInterfaceJSON([]byte(policyJSON), &policy); err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return policy, nil
    74  }
    75  
    76  func parseAuthorization(codec codec.Codec, authorizationJSON string) (foundation.Authorization, error) {
    77  	var authorization foundation.Authorization
    78  	if err := codec.UnmarshalInterfaceJSON([]byte(authorizationJSON), &authorization); err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	return authorization, nil
    83  }
    84  
    85  func execFromString(execStr string) foundation.Exec {
    86  	exec := foundation.Exec_EXEC_UNSPECIFIED
    87  	switch execStr {
    88  	case ExecTry:
    89  		exec = foundation.Exec_EXEC_TRY
    90  	}
    91  	return exec
    92  }
    93  
    94  // VoteOptionFromString returns a VoteOption from a string. It returns an error
    95  // if the string is invalid.
    96  func voteOptionFromString(str string) (foundation.VoteOption, error) {
    97  	vo, ok := foundation.VoteOption_value[str]
    98  	if !ok {
    99  		return foundation.VOTE_OPTION_UNSPECIFIED, fmt.Errorf("'%s' is not a valid vote option", str)
   100  	}
   101  	return foundation.VoteOption(vo), nil
   102  }
   103  
   104  func parseMsgs(cdc codec.Codec, msgsJSON string) ([]sdk.Msg, error) {
   105  	var cliMsgs []json.RawMessage
   106  	if err := json.Unmarshal([]byte(msgsJSON), &cliMsgs); err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	msgs := make([]sdk.Msg, len(cliMsgs))
   111  	for i, anyJSON := range cliMsgs {
   112  		var msg sdk.Msg
   113  		err := cdc.UnmarshalInterfaceJSON(anyJSON, &msg)
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  
   118  		msgs[i] = msg
   119  	}
   120  
   121  	return msgs, nil
   122  }
   123  
   124  // NewTxCmd returns the transaction commands for this module
   125  func NewTxCmd() *cobra.Command {
   126  	txCmd := &cobra.Command{
   127  		Use:                        foundation.ModuleName,
   128  		Short:                      fmt.Sprintf("%s transactions subcommands", foundation.ModuleName),
   129  		DisableFlagParsing:         true,
   130  		SuggestionsMinimumDistance: 2,
   131  		RunE:                       client.ValidateCmd,
   132  	}
   133  
   134  	txCmd.AddCommand(
   135  		NewTxCmdFundTreasury(),
   136  		NewTxCmdWithdrawFromTreasury(),
   137  		NewTxCmdUpdateMembers(),
   138  		NewTxCmdUpdateDecisionPolicy(),
   139  		NewTxCmdSubmitProposal(),
   140  		NewTxCmdWithdrawProposal(),
   141  		NewTxCmdVote(),
   142  		NewTxCmdExec(),
   143  		NewTxCmdLeaveFoundation(),
   144  		NewTxCmdGrant(),
   145  		NewTxCmdRevoke(),
   146  	)
   147  
   148  	return txCmd
   149  }
   150  
   151  func NewTxCmdFundTreasury() *cobra.Command {
   152  	cmd := &cobra.Command{
   153  		Use:   "fund-treasury [from] [amount]",
   154  		Args:  cobra.ExactArgs(2),
   155  		Short: "Fund the treasury",
   156  		Long: `Fund the treasury
   157  `,
   158  		RunE: func(cmd *cobra.Command, args []string) error {
   159  			from := args[0]
   160  			if err := cmd.Flags().Set(flags.FlagFrom, from); err != nil {
   161  				return err
   162  			}
   163  
   164  			clientCtx, err := client.GetClientTxContext(cmd)
   165  			if err != nil {
   166  				return err
   167  			}
   168  
   169  			amount, err := sdk.ParseCoinsNormalized(args[1])
   170  			if err != nil {
   171  				return err
   172  			}
   173  
   174  			msg := foundation.MsgFundTreasury{
   175  				From:   from,
   176  				Amount: amount,
   177  			}
   178  			if err := msg.ValidateBasic(); err != nil {
   179  				return err
   180  			}
   181  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   182  		},
   183  	}
   184  
   185  	flags.AddTxFlagsToCmd(cmd)
   186  	return cmd
   187  }
   188  
   189  func NewTxCmdWithdrawFromTreasury() *cobra.Command {
   190  	cmd := &cobra.Command{
   191  		Use:   "withdraw-from-treasury [authority] [to] [amount]",
   192  		Args:  cobra.ExactArgs(3),
   193  		Short: "Withdraw coins from the treasury",
   194  		Long: `Withdraw coins from the treasury
   195  `,
   196  		RunE: func(cmd *cobra.Command, args []string) error {
   197  			if err := validateGenerateOnly(cmd); err != nil {
   198  				return err
   199  			}
   200  
   201  			clientCtx, err := client.GetClientTxContext(cmd)
   202  			if err != nil {
   203  				return err
   204  			}
   205  
   206  			amount, err := sdk.ParseCoinsNormalized(args[2])
   207  			if err != nil {
   208  				return err
   209  			}
   210  
   211  			msg := foundation.MsgWithdrawFromTreasury{
   212  				Authority: args[0],
   213  				To:        args[1],
   214  				Amount:    amount,
   215  			}
   216  			if err := msg.ValidateBasic(); err != nil {
   217  				return err
   218  			}
   219  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   220  		},
   221  	}
   222  
   223  	flags.AddTxFlagsToCmd(cmd)
   224  	return cmd
   225  }
   226  
   227  func NewTxCmdUpdateMembers() *cobra.Command {
   228  	cmd := &cobra.Command{
   229  		Use:   "update-members [authority] [members-json]",
   230  		Args:  cobra.ExactArgs(2),
   231  		Short: "Update the foundation members",
   232  		Long: `Update the foundation members
   233  
   234  Example of the content of members-json:
   235  
   236  [
   237    {
   238      "address": "addr1",
   239      "participating": true,
   240      "metadata": "some new metadata"
   241    },
   242    {
   243      "address": "addr2",
   244      "participating": false,
   245      "metadata": "some metadata"
   246    }
   247  ]
   248  
   249  Set a member's participating to false to delete it.
   250  `,
   251  		RunE: func(cmd *cobra.Command, args []string) error {
   252  			if err := validateGenerateOnly(cmd); err != nil {
   253  				return err
   254  			}
   255  
   256  			clientCtx, err := client.GetClientTxContext(cmd)
   257  			if err != nil {
   258  				return err
   259  			}
   260  
   261  			updates, err := parseMemberRequests(clientCtx.Codec, args[1])
   262  			if err != nil {
   263  				return err
   264  			}
   265  
   266  			msg := foundation.MsgUpdateMembers{
   267  				Authority:     args[0],
   268  				MemberUpdates: updates,
   269  			}
   270  			if err := msg.ValidateBasic(); err != nil {
   271  				return err
   272  			}
   273  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   274  		},
   275  	}
   276  
   277  	flags.AddTxFlagsToCmd(cmd)
   278  	return cmd
   279  }
   280  
   281  func NewTxCmdUpdateDecisionPolicy() *cobra.Command {
   282  	cmd := &cobra.Command{
   283  		Use:   "update-decision-policy [authority] [policy-json]",
   284  		Args:  cobra.ExactArgs(2),
   285  		Short: "Update the foundation decision policy",
   286  		Long: `Update the foundation decision policy
   287  
   288  Example of the content of policy-json:
   289  
   290  {
   291    "@type": "/lbm.foundation.v1.ThresholdDecisionPolicy",
   292    "threshold": "10",
   293    "windows": {
   294      "voting_period": "24h",
   295      "min_execution_period": "0s"
   296    }
   297  }
   298  `,
   299  		RunE: func(cmd *cobra.Command, args []string) error {
   300  			if err := validateGenerateOnly(cmd); err != nil {
   301  				return err
   302  			}
   303  
   304  			clientCtx, err := client.GetClientTxContext(cmd)
   305  			if err != nil {
   306  				return err
   307  			}
   308  
   309  			msg := foundation.MsgUpdateDecisionPolicy{
   310  				Authority: args[0],
   311  			}
   312  			policy, err := parseDecisionPolicy(clientCtx.Codec, args[1])
   313  			if err != nil {
   314  				return err
   315  			}
   316  			if err := msg.SetDecisionPolicy(policy); err != nil {
   317  				return err
   318  			}
   319  
   320  			if err := msg.ValidateBasic(); err != nil {
   321  				return err
   322  			}
   323  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   324  		},
   325  	}
   326  
   327  	flags.AddTxFlagsToCmd(cmd)
   328  	return cmd
   329  }
   330  
   331  func NewTxCmdSubmitProposal() *cobra.Command {
   332  	cmd := &cobra.Command{
   333  		Use:   "submit-proposal [metadata] [proposers-json] [messages-json]",
   334  		Args:  cobra.ExactArgs(3),
   335  		Short: "Submit a new proposal",
   336  		Long: `Submit a new proposal
   337  
   338  Parameters:
   339      metadata: metadata of the proposal.
   340      proposers-json: the addresses of the proposers in json format.
   341      messages-json: messages in json format that will be executed if the proposal is accepted.
   342  
   343  Example of the content of proposers-json:
   344  
   345  [
   346    "addr1",
   347    "addr2"
   348  ]
   349  
   350  Example of the content of messages-json:
   351  
   352  [
   353    {
   354      "@type": "/lbm.foundation.v1.MsgWithdrawFromTreasury",
   355      "authority": "addr1",
   356      "to": "addr2",
   357      "amount": "10000stake"
   358    }
   359  ]
   360  `,
   361  		RunE: func(cmd *cobra.Command, args []string) error {
   362  			proposers, err := parseAddresses(args[1])
   363  			if err != nil {
   364  				return err
   365  			}
   366  
   367  			signer := proposers[0]
   368  			if err := cmd.Flags().Set(flags.FlagFrom, signer); err != nil {
   369  				return err
   370  			}
   371  
   372  			clientCtx, err := client.GetClientTxContext(cmd)
   373  			if err != nil {
   374  				return err
   375  			}
   376  
   377  			messages, err := parseMsgs(clientCtx.Codec, args[2])
   378  			if err != nil {
   379  				return err
   380  			}
   381  
   382  			execStr, err := cmd.Flags().GetString(FlagExec)
   383  			if err != nil {
   384  				return err
   385  			}
   386  			exec := execFromString(execStr)
   387  
   388  			msg := foundation.MsgSubmitProposal{
   389  				Proposers: proposers,
   390  				Metadata:  args[0],
   391  				Exec:      exec,
   392  			}
   393  			if err := msg.SetMsgs(messages); err != nil {
   394  				return err
   395  			}
   396  			if err := msg.ValidateBasic(); err != nil {
   397  				return err
   398  			}
   399  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   400  		},
   401  	}
   402  
   403  	flags.AddTxFlagsToCmd(cmd)
   404  	cmd.Flags().String(FlagExec, "", "Set to 'try' to try to execute proposal immediately after creation (proposers signatures are considered as Yes votes)")
   405  
   406  	return cmd
   407  }
   408  
   409  func NewTxCmdWithdrawProposal() *cobra.Command {
   410  	cmd := &cobra.Command{
   411  		Use:   "withdraw-proposal [proposal-id] [address]",
   412  		Args:  cobra.ExactArgs(2),
   413  		Short: "Withdraw a submitted proposal",
   414  		Long: `Withdraw a submitted proposal.
   415  
   416  Parameters:
   417      proposal-id: unique ID of the proposal.
   418      address: one of the proposer of the proposal.
   419  `,
   420  		RunE: func(cmd *cobra.Command, args []string) error {
   421  			address := args[1]
   422  			if err := cmd.Flags().Set(flags.FlagFrom, address); err != nil {
   423  				return err
   424  			}
   425  
   426  			clientCtx, err := client.GetClientTxContext(cmd)
   427  			if err != nil {
   428  				return err
   429  			}
   430  
   431  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   432  			if err != nil {
   433  				return err
   434  			}
   435  
   436  			msg := foundation.MsgWithdrawProposal{
   437  				ProposalId: proposalID,
   438  				Address:    address,
   439  			}
   440  			if err := msg.ValidateBasic(); err != nil {
   441  				return err
   442  			}
   443  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   444  		},
   445  	}
   446  
   447  	flags.AddTxFlagsToCmd(cmd)
   448  	return cmd
   449  }
   450  
   451  func NewTxCmdVote() *cobra.Command {
   452  	cmd := &cobra.Command{
   453  		Use:   "vote [proposal-id] [voter] [option] [metadata]",
   454  		Args:  cobra.ExactArgs(4),
   455  		Short: "Vote on a proposal",
   456  		Long: `Vote on a proposal.
   457  
   458  Parameters:
   459      proposal-id: unique ID of the proposal
   460      voter: voter account addresses.
   461      vote-option: choice of the voter(s)
   462          VOTE_OPTION_UNSPECIFIED: no-op
   463          VOTE_OPTION_NO: no
   464          VOTE_OPTION_YES: yes
   465          VOTE_OPTION_ABSTAIN: abstain
   466          VOTE_OPTION_NO_WITH_VETO: no-with-veto
   467      metadata: metadata for the vote
   468  `,
   469  		RunE: func(cmd *cobra.Command, args []string) error {
   470  			voter := args[1]
   471  			if err := cmd.Flags().Set(flags.FlagFrom, voter); err != nil {
   472  				return err
   473  			}
   474  
   475  			clientCtx, err := client.GetClientTxContext(cmd)
   476  			if err != nil {
   477  				return err
   478  			}
   479  
   480  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   481  			if err != nil {
   482  				return err
   483  			}
   484  
   485  			option, err := voteOptionFromString(args[2])
   486  			if err != nil {
   487  				return err
   488  			}
   489  
   490  			execStr, err := cmd.Flags().GetString(FlagExec)
   491  			if err != nil {
   492  				return err
   493  			}
   494  			exec := execFromString(execStr)
   495  
   496  			msg := foundation.MsgVote{
   497  				ProposalId: proposalID,
   498  				Voter:      voter,
   499  				Option:     option,
   500  				Metadata:   args[3],
   501  				Exec:       exec,
   502  			}
   503  			if err := msg.ValidateBasic(); err != nil {
   504  				return err
   505  			}
   506  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   507  		},
   508  	}
   509  
   510  	flags.AddTxFlagsToCmd(cmd)
   511  	cmd.Flags().String(FlagExec, "", "Set to 'try' to try to execute proposal immediately after voting")
   512  
   513  	return cmd
   514  }
   515  
   516  func NewTxCmdExec() *cobra.Command {
   517  	cmd := &cobra.Command{
   518  		Use:   "exec [proposal-id] [signer]",
   519  		Args:  cobra.ExactArgs(2),
   520  		Short: "Execute a proposal",
   521  		RunE: func(cmd *cobra.Command, args []string) error {
   522  			signer := args[1]
   523  			if err := cmd.Flags().Set(flags.FlagFrom, signer); err != nil {
   524  				return err
   525  			}
   526  
   527  			clientCtx, err := client.GetClientTxContext(cmd)
   528  			if err != nil {
   529  				return err
   530  			}
   531  
   532  			proposalID, err := strconv.ParseUint(args[0], 10, 64)
   533  			if err != nil {
   534  				return err
   535  			}
   536  
   537  			msg := foundation.MsgExec{
   538  				ProposalId: proposalID,
   539  				Signer:     signer,
   540  			}
   541  			if err := msg.ValidateBasic(); err != nil {
   542  				return err
   543  			}
   544  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   545  		},
   546  	}
   547  
   548  	flags.AddTxFlagsToCmd(cmd)
   549  	return cmd
   550  }
   551  
   552  func NewTxCmdLeaveFoundation() *cobra.Command {
   553  	cmd := &cobra.Command{
   554  		Use:   "leave-foundation [address]",
   555  		Args:  cobra.ExactArgs(1),
   556  		Short: "Leave the foundation",
   557  		RunE: func(cmd *cobra.Command, args []string) error {
   558  			address := args[0]
   559  			if err := cmd.Flags().Set(flags.FlagFrom, address); err != nil {
   560  				return err
   561  			}
   562  
   563  			clientCtx, err := client.GetClientTxContext(cmd)
   564  			if err != nil {
   565  				return err
   566  			}
   567  
   568  			msg := foundation.MsgLeaveFoundation{
   569  				Address: address,
   570  			}
   571  			if err := msg.ValidateBasic(); err != nil {
   572  				return err
   573  			}
   574  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   575  		},
   576  	}
   577  
   578  	flags.AddTxFlagsToCmd(cmd)
   579  	return cmd
   580  }
   581  
   582  func NewTxCmdGrant() *cobra.Command {
   583  	cmd := &cobra.Command{
   584  		Use:   "grant [authority] [grantee] [authorization-json]",
   585  		Args:  cobra.ExactArgs(3),
   586  		Short: "Grant an authorization to grantee",
   587  		Long: `Grant an authorization to grantee
   588  
   589  Example of the content of authorization-json:
   590  
   591  {
   592    "@type": "/lbm.foundation.v1.ReceiveFromTreasuryAuthorization",
   593    "receive_limit": [
   594      "denom": "stake",
   595      "amount": "10000"
   596    ]
   597  }
   598  `,
   599  		RunE: func(cmd *cobra.Command, args []string) error {
   600  			if err := validateGenerateOnly(cmd); err != nil {
   601  				return err
   602  			}
   603  
   604  			clientCtx, err := client.GetClientTxContext(cmd)
   605  			if err != nil {
   606  				return err
   607  			}
   608  
   609  			msg := foundation.MsgGrant{
   610  				Authority: args[0],
   611  				Grantee:   args[1],
   612  			}
   613  			authorization, err := parseAuthorization(clientCtx.Codec, args[2])
   614  			if err != nil {
   615  				return err
   616  			}
   617  			if err := msg.SetAuthorization(authorization); err != nil {
   618  				return err
   619  			}
   620  
   621  			if err := msg.ValidateBasic(); err != nil {
   622  				return err
   623  			}
   624  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   625  		},
   626  	}
   627  
   628  	flags.AddTxFlagsToCmd(cmd)
   629  	return cmd
   630  }
   631  
   632  func NewTxCmdRevoke() *cobra.Command {
   633  	cmd := &cobra.Command{
   634  		Use:   "revoke [authority] [grantee] [msg-type-url]",
   635  		Args:  cobra.ExactArgs(3),
   636  		Short: "Revoke an authorization of grantee",
   637  		Long: `Revoke an authorization of grantee
   638  `,
   639  		RunE: func(cmd *cobra.Command, args []string) error {
   640  			if err := validateGenerateOnly(cmd); err != nil {
   641  				return err
   642  			}
   643  
   644  			clientCtx, err := client.GetClientTxContext(cmd)
   645  			if err != nil {
   646  				return err
   647  			}
   648  
   649  			msg := foundation.MsgRevoke{
   650  				Authority:  args[0],
   651  				Grantee:    args[1],
   652  				MsgTypeUrl: args[2],
   653  			}
   654  			if err := msg.ValidateBasic(); err != nil {
   655  				return err
   656  			}
   657  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
   658  		},
   659  	}
   660  
   661  	flags.AddTxFlagsToCmd(cmd)
   662  	return cmd
   663  }
   664  
   665  // NewProposalCmdFoundationExecProposal returns a CLI command handler for
   666  // creating a foundation exec proposal governance transaction.
   667  func NewProposalCmdFoundationExec() *cobra.Command {
   668  	cmd := &cobra.Command{
   669  		Use:   "foundation-exec [messages-json]",
   670  		Args:  cobra.ExactArgs(1),
   671  		Short: "Submit a foundation exec proposal",
   672  		Long: `
   673  Parameters:
   674      messages-json: messages in json format that will be executed if the proposal is accepted.
   675  
   676  Example of the content of messages-json:
   677  
   678  [
   679    {
   680      "@type": "/lbm.foundation.v1.MsgUpdateCensorship",
   681      "authority": "addr1",
   682      "censorship": {
   683        "msg_type_url": "/cosmos.staking.v1beta1.MsgCreateValidator",
   684        "authority": "CENSORSHIP_AUTHORITY_UNSPECIFIED"
   685      }
   686    }
   687  ]
   688  `,
   689  		RunE: func(cmd *cobra.Command, args []string) error {
   690  			clientCtx, err := client.GetClientTxContext(cmd)
   691  			if err != nil {
   692  				return err
   693  			}
   694  
   695  			from := clientCtx.GetFromAddress()
   696  
   697  			title, err := cmd.Flags().GetString(govcli.FlagTitle)
   698  			if err != nil {
   699  				return err
   700  			}
   701  
   702  			description, err := cmd.Flags().GetString(govcli.FlagDescription)
   703  			if err != nil {
   704  				return err
   705  			}
   706  
   707  			messages, err := parseMsgs(clientCtx.Codec, args[0])
   708  			if err != nil {
   709  				return err
   710  			}
   711  
   712  			content := foundation.NewFoundationExecProposal(title, description, messages)
   713  
   714  			depositStr, err := cmd.Flags().GetString(govcli.FlagDeposit)
   715  			if err != nil {
   716  				return err
   717  			}
   718  			deposit, err := sdk.ParseCoinsNormalized(depositStr)
   719  			if err != nil {
   720  				return err
   721  			}
   722  
   723  			msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from)
   724  			if err != nil {
   725  				return err
   726  			}
   727  			if err := msg.ValidateBasic(); err != nil {
   728  				return err
   729  			}
   730  
   731  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   732  		},
   733  	}
   734  
   735  	cmd.Flags().String(govcli.FlagTitle, "", "title of proposal")
   736  	cmd.Flags().String(govcli.FlagDescription, "", "description of proposal")
   737  	cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal")
   738  
   739  	return cmd
   740  }