github.com/cosmos/cosmos-sdk@v0.50.10/x/group/client/cli/tx.go (about) 1 package cli 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 9 "github.com/spf13/cobra" 10 11 "cosmossdk.io/core/address" 12 13 "github.com/cosmos/cosmos-sdk/client" 14 "github.com/cosmos/cosmos-sdk/client/flags" 15 "github.com/cosmos/cosmos-sdk/client/tx" 16 "github.com/cosmos/cosmos-sdk/version" 17 "github.com/cosmos/cosmos-sdk/x/group" 18 "github.com/cosmos/cosmos-sdk/x/group/internal/math" 19 ) 20 21 const ( 22 FlagExec = "exec" 23 ExecTry = "try" 24 FlagGroupPolicyAsAdmin = "group-policy-as-admin" 25 ) 26 27 var errZeroGroupID = errors.New("group id cannot be 0") 28 29 // TxCmd returns a root CLI command handler for all x/group transaction commands. 30 func TxCmd(name string, ac address.Codec) *cobra.Command { 31 txCmd := &cobra.Command{ 32 Use: name, 33 Short: "Group transaction subcommands", 34 DisableFlagParsing: true, 35 SuggestionsMinimumDistance: 2, 36 RunE: client.ValidateCmd, 37 } 38 39 txCmd.AddCommand( 40 MsgCreateGroupCmd(), 41 MsgUpdateGroupAdminCmd(), 42 MsgUpdateGroupMetadataCmd(), 43 MsgUpdateGroupMembersCmd(), 44 MsgCreateGroupWithPolicyCmd(), 45 MsgCreateGroupPolicyCmd(), 46 MsgUpdateGroupPolicyAdminCmd(), 47 MsgUpdateGroupPolicyDecisionPolicyCmd(ac), 48 MsgUpdateGroupPolicyMetadataCmd(), 49 MsgWithdrawProposalCmd(), 50 MsgSubmitProposalCmd(), 51 MsgVoteCmd(), 52 MsgExecCmd(), 53 MsgLeaveGroupCmd(), 54 NewCmdDraftProposal(), 55 ) 56 57 return txCmd 58 } 59 60 // MsgCreateGroupCmd creates a CLI command for Msg/CreateGroup. 61 func MsgCreateGroupCmd() *cobra.Command { 62 cmd := &cobra.Command{ 63 Use: "create-group [admin] [metadata] [members-json-file]", 64 Short: "Create a group which is an aggregation of member accounts with associated weights and an administrator account.", 65 Long: `Create a group which is an aggregation of member accounts with associated weights and an administrator account. 66 Note, the '--from' flag is ignored as it is implied from [admin]. Members accounts can be given through a members JSON file that contains an array of members.`, 67 Example: fmt.Sprintf(` 68 %s tx group create-group [admin] [metadata] [members-json-file] 69 70 Where members.json contains: 71 72 { 73 "members": [ 74 { 75 "address": "addr1", 76 "weight": "1", 77 "metadata": "some metadata" 78 }, 79 { 80 "address": "addr2", 81 "weight": "1", 82 "metadata": "some metadata" 83 } 84 ] 85 }`, version.AppName), 86 Args: cobra.ExactArgs(3), 87 RunE: func(cmd *cobra.Command, args []string) error { 88 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 89 if err != nil { 90 return err 91 } 92 93 clientCtx, err := client.GetClientTxContext(cmd) 94 if err != nil { 95 return err 96 } 97 98 members, err := parseMembers(args[2]) 99 if err != nil { 100 return err 101 } 102 103 for _, member := range members { 104 if _, err := math.NewPositiveDecFromString(member.Weight); err != nil { 105 return fmt.Errorf("invalid weight %s for %s: weight must be positive", member.Weight, member.Address) 106 } 107 } 108 109 msg := &group.MsgCreateGroup{ 110 Admin: clientCtx.GetFromAddress().String(), 111 Members: members, 112 Metadata: args[1], 113 } 114 115 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 116 }, 117 } 118 119 flags.AddTxFlagsToCmd(cmd) 120 121 return cmd 122 } 123 124 // MsgUpdateGroupMembersCmd creates a CLI command for Msg/UpdateGroupMembers. 125 func MsgUpdateGroupMembersCmd() *cobra.Command { 126 cmd := &cobra.Command{ 127 Use: "update-group-members [admin] [group-id] [members-json-file]", 128 Short: "Update a group's members. Set a member's weight to \"0\" to delete it.", 129 Example: fmt.Sprintf(` 130 %s tx group update-group-members [admin] [group-id] [members-json-file] 131 132 Where members.json contains: 133 134 { 135 "members": [ 136 { 137 "address": "addr1", 138 "weight": "1", 139 "metadata": "some new metadata" 140 }, 141 { 142 "address": "addr2", 143 "weight": "0", 144 "metadata": "some metadata" 145 } 146 ] 147 } 148 149 Set a member's weight to "0" to delete it. 150 `, version.AppName), 151 Args: cobra.ExactArgs(3), 152 RunE: func(cmd *cobra.Command, args []string) error { 153 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 154 if err != nil { 155 return err 156 } 157 158 clientCtx, err := client.GetClientTxContext(cmd) 159 if err != nil { 160 return err 161 } 162 163 members, err := parseMembers(args[2]) 164 if err != nil { 165 return err 166 } 167 168 for _, member := range members { 169 if _, err := math.NewNonNegativeDecFromString(member.Weight); err != nil { 170 return fmt.Errorf("invalid weight %s for %s: weight must not be negative", member.Weight, member.Address) 171 } 172 } 173 174 groupID, err := strconv.ParseUint(args[1], 10, 64) 175 if err != nil { 176 return err 177 } 178 179 if groupID == 0 { 180 return errZeroGroupID 181 } 182 183 msg := &group.MsgUpdateGroupMembers{ 184 Admin: clientCtx.GetFromAddress().String(), 185 MemberUpdates: members, 186 GroupId: groupID, 187 } 188 189 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 190 }, 191 } 192 193 flags.AddTxFlagsToCmd(cmd) 194 195 return cmd 196 } 197 198 // MsgUpdateGroupAdminCmd creates a CLI command for Msg/UpdateGroupAdmin. 199 func MsgUpdateGroupAdminCmd() *cobra.Command { 200 cmd := &cobra.Command{ 201 Use: "update-group-admin [admin] [group-id] [new-admin]", 202 Short: "Update a group's admin", 203 Args: cobra.ExactArgs(3), 204 RunE: func(cmd *cobra.Command, args []string) error { 205 if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil { 206 return err 207 } 208 209 clientCtx, err := client.GetClientTxContext(cmd) 210 if err != nil { 211 return err 212 } 213 214 groupID, err := strconv.ParseUint(args[1], 10, 64) 215 if err != nil { 216 return err 217 } 218 219 if groupID == 0 { 220 return errZeroGroupID 221 } 222 223 if strings.EqualFold(args[0], args[2]) { 224 return errors.New("new admin cannot be the same as the current admin") 225 } 226 227 msg := &group.MsgUpdateGroupAdmin{ 228 Admin: clientCtx.GetFromAddress().String(), 229 NewAdmin: args[2], 230 GroupId: groupID, 231 } 232 233 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 234 }, 235 } 236 237 flags.AddTxFlagsToCmd(cmd) 238 239 return cmd 240 } 241 242 // MsgUpdateGroupMetadataCmd creates a CLI command for Msg/UpdateGroupMetadata. 243 func MsgUpdateGroupMetadataCmd() *cobra.Command { 244 cmd := &cobra.Command{ 245 Use: "update-group-metadata [admin] [group-id] [metadata]", 246 Short: "Update a group's metadata", 247 Args: cobra.ExactArgs(3), 248 RunE: func(cmd *cobra.Command, args []string) error { 249 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 250 if err != nil { 251 return err 252 } 253 254 clientCtx, err := client.GetClientTxContext(cmd) 255 if err != nil { 256 return err 257 } 258 259 groupID, err := strconv.ParseUint(args[1], 10, 64) 260 if err != nil { 261 return err 262 } 263 264 if groupID == 0 { 265 return errZeroGroupID 266 } 267 268 msg := &group.MsgUpdateGroupMetadata{ 269 Admin: clientCtx.GetFromAddress().String(), 270 Metadata: args[2], 271 GroupId: groupID, 272 } 273 274 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 275 }, 276 } 277 278 flags.AddTxFlagsToCmd(cmd) 279 280 return cmd 281 } 282 283 // MsgCreateGroupWithPolicyCmd creates a CLI command for Msg/CreateGroupWithPolicy. 284 func MsgCreateGroupWithPolicyCmd() *cobra.Command { 285 cmd := &cobra.Command{ 286 Use: "create-group-with-policy [admin] [group-metadata] [group-policy-metadata] [members-json-file] [decision-policy-json-file]", 287 Short: "Create a group with policy which is an aggregation of member accounts with associated weights, an administrator account and decision policy.", 288 Long: `Create a group with policy which is an aggregation of member accounts with associated weights, 289 an administrator account and decision policy. Note, the '--from' flag is ignored as it is implied from [admin]. 290 Members accounts can be given through a members JSON file that contains an array of members. 291 If group-policy-as-admin flag is set to true, the admin of the newly created group and group policy is set with the group policy address itself.`, 292 Example: fmt.Sprintf(` 293 %s tx group create-group-with-policy [admin] [group-metadata] [group-policy-metadata] members.json policy.json 294 295 where members.json contains: 296 297 { 298 "members": [ 299 { 300 "address": "addr1", 301 "weight": "1", 302 "metadata": "some metadata" 303 }, 304 { 305 "address": "addr2", 306 "weight": "1", 307 "metadata": "some metadata" 308 } 309 ] 310 } 311 312 and policy.json contains: 313 314 { 315 "@type": "/cosmos.group.v1.ThresholdDecisionPolicy", 316 "threshold": "1", 317 "windows": { 318 "voting_period": "120h", 319 "min_execution_period": "0s" 320 } 321 } 322 `, version.AppName), 323 Args: cobra.MinimumNArgs(5), 324 RunE: func(cmd *cobra.Command, args []string) error { 325 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 326 if err != nil { 327 return err 328 } 329 330 clientCtx, err := client.GetClientTxContext(cmd) 331 if err != nil { 332 return err 333 } 334 335 groupPolicyAsAdmin, err := cmd.Flags().GetBool(FlagGroupPolicyAsAdmin) 336 if err != nil { 337 return err 338 } 339 340 members, err := parseMembers(args[3]) 341 if err != nil { 342 return err 343 } 344 345 for _, member := range members { 346 if _, err := math.NewPositiveDecFromString(member.Weight); err != nil { 347 return fmt.Errorf("invalid weight %s for %s: weight must be positive", member.Weight, member.Address) 348 } 349 } 350 351 policy, err := parseDecisionPolicy(clientCtx.Codec, args[4]) 352 if err != nil { 353 return err 354 } 355 356 if err := policy.ValidateBasic(); err != nil { 357 return err 358 } 359 360 msg, err := group.NewMsgCreateGroupWithPolicy( 361 clientCtx.GetFromAddress().String(), 362 members, 363 args[1], 364 args[2], 365 groupPolicyAsAdmin, 366 policy, 367 ) 368 if err != nil { 369 return err 370 } 371 372 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 373 }, 374 } 375 cmd.Flags().Bool(FlagGroupPolicyAsAdmin, false, "Sets admin of the newly created group and group policy with group policy address itself when true") 376 flags.AddTxFlagsToCmd(cmd) 377 378 return cmd 379 } 380 381 // MsgCreateGroupPolicyCmd creates a CLI command for Msg/CreateGroupPolicy. 382 func MsgCreateGroupPolicyCmd() *cobra.Command { 383 cmd := &cobra.Command{ 384 Use: "create-group-policy [admin] [group-id] [metadata] [decision-policy-json-file]", 385 Short: `Create a group policy which is an account associated with a group and a decision policy. Note, the '--from' flag is ignored as it is implied from [admin].`, 386 Example: fmt.Sprintf(` 387 %s tx group create-group-policy [admin] [group-id] [metadata] policy.json 388 389 where policy.json contains: 390 391 { 392 "@type": "/cosmos.group.v1.ThresholdDecisionPolicy", 393 "threshold": "1", 394 "windows": { 395 "voting_period": "120h", 396 "min_execution_period": "0s" 397 } 398 } 399 400 Here, we can use percentage decision policy when needed, where 0 < percentage <= 1: 401 402 { 403 "@type": "/cosmos.group.v1.PercentageDecisionPolicy", 404 "percentage": "0.5", 405 "windows": { 406 "voting_period": "120h", 407 "min_execution_period": "0s" 408 } 409 }`, version.AppName), 410 Args: cobra.ExactArgs(4), 411 RunE: func(cmd *cobra.Command, args []string) error { 412 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 413 if err != nil { 414 return err 415 } 416 417 clientCtx, err := client.GetClientTxContext(cmd) 418 if err != nil { 419 return err 420 } 421 422 groupID, err := strconv.ParseUint(args[1], 10, 64) 423 if err != nil { 424 return err 425 } 426 427 if groupID == 0 { 428 return errZeroGroupID 429 } 430 431 policy, err := parseDecisionPolicy(clientCtx.Codec, args[3]) 432 if err != nil { 433 return err 434 } 435 436 if err := policy.ValidateBasic(); err != nil { 437 return err 438 } 439 440 msg, err := group.NewMsgCreateGroupPolicy( 441 clientCtx.GetFromAddress(), 442 groupID, 443 args[2], 444 policy, 445 ) 446 if err != nil { 447 return err 448 } 449 450 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 451 }, 452 } 453 454 flags.AddTxFlagsToCmd(cmd) 455 456 return cmd 457 } 458 459 // MsgUpdateGroupPolicyAdminCmd creates a CLI command for Msg/UpdateGroupPolicyAdmin. 460 func MsgUpdateGroupPolicyAdminCmd() *cobra.Command { 461 cmd := &cobra.Command{ 462 Use: "update-group-policy-admin [admin] [group-policy-account] [new-admin]", 463 Short: "Update a group policy admin", 464 Args: cobra.ExactArgs(3), 465 RunE: func(cmd *cobra.Command, args []string) error { 466 if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil { 467 return err 468 } 469 470 if strings.EqualFold(args[0], args[2]) { 471 return errors.New("new admin cannot be the same as the current admin") 472 } 473 474 clientCtx, err := client.GetClientTxContext(cmd) 475 if err != nil { 476 return err 477 } 478 479 msg := &group.MsgUpdateGroupPolicyAdmin{ 480 Admin: clientCtx.GetFromAddress().String(), 481 GroupPolicyAddress: args[1], 482 NewAdmin: args[2], 483 } 484 485 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 486 }, 487 } 488 489 flags.AddTxFlagsToCmd(cmd) 490 491 return cmd 492 } 493 494 // MsgUpdateGroupPolicyDecisionPolicyCmd creates a CLI command for Msg/UpdateGroupPolicyDecisionPolicy. 495 func MsgUpdateGroupPolicyDecisionPolicyCmd(ac address.Codec) *cobra.Command { 496 cmd := &cobra.Command{ 497 Use: "update-group-policy-decision-policy [admin] [group-policy-account] [decision-policy-json-file]", 498 Short: "Update a group policy's decision policy", 499 Args: cobra.ExactArgs(3), 500 RunE: func(cmd *cobra.Command, args []string) error { 501 err := cmd.Flags().Set(flags.FlagFrom, args[0]) 502 if err != nil { 503 return err 504 } 505 506 clientCtx, err := client.GetClientTxContext(cmd) 507 if err != nil { 508 return err 509 } 510 511 policy, err := parseDecisionPolicy(clientCtx.Codec, args[2]) 512 if err != nil { 513 return err 514 } 515 516 accountAddress, err := ac.StringToBytes(args[1]) 517 if err != nil { 518 return err 519 } 520 521 msg, err := group.NewMsgUpdateGroupPolicyDecisionPolicy( 522 clientCtx.GetFromAddress(), 523 accountAddress, 524 policy, 525 ) 526 if err != nil { 527 return err 528 } 529 530 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 531 }, 532 } 533 534 flags.AddTxFlagsToCmd(cmd) 535 536 return cmd 537 } 538 539 // MsgUpdateGroupPolicyMetadataCmd creates a CLI command for Msg/UpdateGroupPolicyMetadata. 540 func MsgUpdateGroupPolicyMetadataCmd() *cobra.Command { 541 cmd := &cobra.Command{ 542 Use: "update-group-policy-metadata [admin] [group-policy-account] [new-metadata]", 543 Short: "Update a group policy metadata", 544 Args: cobra.ExactArgs(3), 545 RunE: func(cmd *cobra.Command, args []string) error { 546 if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil { 547 return err 548 } 549 550 clientCtx, err := client.GetClientTxContext(cmd) 551 if err != nil { 552 return err 553 } 554 555 msg := &group.MsgUpdateGroupPolicyMetadata{ 556 Admin: clientCtx.GetFromAddress().String(), 557 GroupPolicyAddress: args[1], 558 Metadata: args[2], 559 } 560 561 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 562 }, 563 } 564 565 flags.AddTxFlagsToCmd(cmd) 566 567 return cmd 568 } 569 570 // MsgSubmitProposalCmd creates a CLI command for Msg/SubmitProposal. 571 func MsgSubmitProposalCmd() *cobra.Command { 572 cmd := &cobra.Command{ 573 Use: "submit-proposal [proposal_json_file]", 574 Short: "Submit a new proposal", 575 Long: `Submit a new proposal. 576 Parameters: 577 msg_tx_json_file: path to json file with messages that will be executed if the proposal is accepted.`, 578 Example: fmt.Sprintf(` 579 %s tx group submit-proposal path/to/proposal.json 580 581 Where proposal.json contains: 582 583 { 584 "group_policy_address": "cosmos1...", 585 // array of proto-JSON-encoded sdk.Msgs 586 "messages": [ 587 { 588 "@type": "/cosmos.bank.v1beta1.MsgSend", 589 "from_address": "cosmos1...", 590 "to_address": "cosmos1...", 591 "amount":[{"denom": "stake","amount": "10"}] 592 } 593 ], 594 // metadata can be any of base64 encoded, raw text, stringified json, IPFS link to json 595 // see below for example metadata 596 "metadata": "4pIMOgIGx1vZGU=", // base64-encoded metadata 597 "title": "My proposal", 598 "summary": "This is a proposal to send 10 stake to cosmos1...", 599 "proposers": ["cosmos1...", "cosmos1..."], 600 } 601 602 metadata example: 603 { 604 "title": "", 605 "authors": [""], 606 "summary": "", 607 "details": "", 608 "proposal_forum_url": "", 609 "vote_option_context": "", 610 } 611 `, version.AppName), 612 Args: cobra.ExactArgs(1), 613 RunE: func(cmd *cobra.Command, args []string) error { 614 prop, err := getCLIProposal(args[0]) 615 if err != nil { 616 return err 617 } 618 619 // Since the --from flag is not required on this CLI command, we 620 // ignore it, and just use the 1st proposer in the JSON file. 621 if prop.Proposers == nil || len(prop.Proposers) == 0 { 622 return errors.New("no proposers specified in proposal") 623 } 624 cmd.Flags().Set(flags.FlagFrom, prop.Proposers[0]) 625 626 clientCtx, err := client.GetClientTxContext(cmd) 627 if err != nil { 628 return err 629 } 630 631 msgs, err := parseMsgs(clientCtx.Codec, prop) 632 if err != nil { 633 return err 634 } 635 636 execStr, _ := cmd.Flags().GetString(FlagExec) 637 msg, err := group.NewMsgSubmitProposal( 638 prop.GroupPolicyAddress, 639 prop.Proposers, 640 msgs, 641 prop.Metadata, 642 execFromString(execStr), 643 prop.Title, 644 prop.Summary, 645 ) 646 if err != nil { 647 return err 648 } 649 650 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 651 }, 652 } 653 654 cmd.Flags().String(FlagExec, "", "Set to 1 to try to execute proposal immediately after creation (proposers signatures are considered as Yes votes)") 655 flags.AddTxFlagsToCmd(cmd) 656 657 return cmd 658 } 659 660 // MsgWithdrawProposalCmd creates a CLI command for Msg/WithdrawProposal. 661 func MsgWithdrawProposalCmd() *cobra.Command { 662 cmd := &cobra.Command{ 663 Use: "withdraw-proposal [proposal-id] [group-policy-admin-or-proposer]", 664 Short: "Withdraw a submitted proposal", 665 Long: `Withdraw a submitted proposal. 666 667 Parameters: 668 proposal-id: unique ID of the proposal. 669 group-policy-admin-or-proposer: either admin of the group policy or one the proposer of the proposal. 670 Note: --from flag will be ignored here. 671 `, 672 Args: cobra.ExactArgs(2), 673 RunE: func(cmd *cobra.Command, args []string) error { 674 if err := cmd.Flags().Set(flags.FlagFrom, args[1]); err != nil { 675 return err 676 } 677 678 clientCtx, err := client.GetClientTxContext(cmd) 679 if err != nil { 680 return err 681 } 682 683 proposalID, err := strconv.ParseUint(args[0], 10, 64) 684 if err != nil { 685 return err 686 } 687 688 if proposalID == 0 { 689 return fmt.Errorf("invalid proposal id: %d", proposalID) 690 } 691 692 msg := &group.MsgWithdrawProposal{ 693 ProposalId: proposalID, 694 Address: clientCtx.GetFromAddress().String(), 695 } 696 697 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 698 }, 699 } 700 701 flags.AddTxFlagsToCmd(cmd) 702 703 return cmd 704 } 705 706 // MsgVoteCmd creates a CLI command for Msg/Vote. 707 func MsgVoteCmd() *cobra.Command { 708 cmd := &cobra.Command{ 709 Use: "vote [proposal-id] [voter] [vote-option] [metadata]", 710 Short: "Vote on a proposal", 711 Long: `Vote on a proposal. 712 713 Parameters: 714 proposal-id: unique ID of the proposal 715 voter: voter account addresses. 716 vote-option: choice of the voter(s) 717 VOTE_OPTION_UNSPECIFIED: no-op 718 VOTE_OPTION_NO: no 719 VOTE_OPTION_YES: yes 720 VOTE_OPTION_ABSTAIN: abstain 721 VOTE_OPTION_NO_WITH_VETO: no-with-veto 722 Metadata: metadata for the vote 723 `, 724 Args: cobra.ExactArgs(4), 725 RunE: func(cmd *cobra.Command, args []string) error { 726 err := cmd.Flags().Set(flags.FlagFrom, args[1]) 727 if err != nil { 728 return err 729 } 730 731 clientCtx, err := client.GetClientTxContext(cmd) 732 if err != nil { 733 return err 734 } 735 736 proposalID, err := strconv.ParseUint(args[0], 10, 64) 737 if err != nil { 738 return err 739 } 740 741 voteOption, err := group.VoteOptionFromString(args[2]) 742 if err != nil { 743 return err 744 } 745 746 execStr, _ := cmd.Flags().GetString(FlagExec) 747 748 msg := &group.MsgVote{ 749 ProposalId: proposalID, 750 Voter: args[1], 751 Option: voteOption, 752 Metadata: args[3], 753 Exec: execFromString(execStr), 754 } 755 756 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 757 }, 758 } 759 760 cmd.Flags().String(FlagExec, "", "Set to 1 to try to execute proposal immediately after voting") 761 flags.AddTxFlagsToCmd(cmd) 762 763 return cmd 764 } 765 766 // MsgExecCmd creates a CLI command for Msg/Exec. 767 func MsgExecCmd() *cobra.Command { 768 cmd := &cobra.Command{ 769 Use: "exec [proposal-id]", 770 Short: "Execute a proposal", 771 Args: cobra.ExactArgs(1), 772 RunE: func(cmd *cobra.Command, args []string) error { 773 clientCtx, err := client.GetClientTxContext(cmd) 774 if err != nil { 775 return err 776 } 777 778 proposalID, err := strconv.ParseUint(args[0], 10, 64) 779 if err != nil { 780 return err 781 } 782 783 msg := &group.MsgExec{ 784 ProposalId: proposalID, 785 Executor: clientCtx.GetFromAddress().String(), 786 } 787 788 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 789 }, 790 } 791 792 flags.AddTxFlagsToCmd(cmd) 793 794 return cmd 795 } 796 797 // MsgLeaveGroupCmd creates a CLI command for Msg/LeaveGroup. 798 func MsgLeaveGroupCmd() *cobra.Command { 799 cmd := &cobra.Command{ 800 Use: "leave-group [member-address] [group-id]", 801 Short: "Remove member from the group", 802 Long: `Remove member from the group 803 804 Parameters: 805 group-id: unique id of the group 806 member-address: account address of the group member 807 Note, the '--from' flag is ignored as it is implied from [member-address] 808 `, 809 Args: cobra.ExactArgs(2), 810 RunE: func(cmd *cobra.Command, args []string) error { 811 cmd.Flags().Set(flags.FlagFrom, args[0]) 812 clientCtx, err := client.GetClientTxContext(cmd) 813 if err != nil { 814 return err 815 } 816 817 groupID, err := strconv.ParseUint(args[1], 10, 64) 818 if err != nil { 819 return err 820 } 821 822 if groupID == 0 { 823 return errZeroGroupID 824 } 825 826 msg := &group.MsgLeaveGroup{ 827 Address: clientCtx.GetFromAddress().String(), 828 GroupId: groupID, 829 } 830 831 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 832 }, 833 } 834 835 flags.AddTxFlagsToCmd(cmd) 836 837 return cmd 838 }