docs: Add docs for group module (#10800)
## Description Closes: #9902 Add docs for group module --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
This commit is contained in:
parent
59793e68fd
commit
7521bcc5f8
|
@ -0,0 +1,74 @@
|
||||||
|
<!--
|
||||||
|
order: 1
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Concepts
|
||||||
|
|
||||||
|
## Group
|
||||||
|
|
||||||
|
A group is simply an aggregation of accounts with associated weights. It is not
|
||||||
|
an account and doesn't have a balance. It doesn't in and of itself have any
|
||||||
|
sort of voting or decision weight. It does have an "administrator" which has
|
||||||
|
the ability to add, remove and update members in the group. Note that a
|
||||||
|
group account could be an administrator of a group.
|
||||||
|
|
||||||
|
## Group Account
|
||||||
|
|
||||||
|
A group account is an account associated with a group and a decision policy.
|
||||||
|
Group accounts are abstracted from groups because a single group may have
|
||||||
|
multiple decision policies for different types of actions. Managing group
|
||||||
|
membership separately from decision policies results in the least overhead
|
||||||
|
and keeps membership consistent across different policies. The pattern that
|
||||||
|
is recommended is to have a single master group account for a given group,
|
||||||
|
and then to create separate group accounts with different decision policies
|
||||||
|
and delegate the desired permissions from the master account to
|
||||||
|
those "sub-accounts" using the `x/authz` module.
|
||||||
|
|
||||||
|
## Decision Policy
|
||||||
|
|
||||||
|
A decision policy is the mechanism by which members of a group can vote on
|
||||||
|
proposals.
|
||||||
|
|
||||||
|
All decision policies generally would have a minimum and maximum voting window.
|
||||||
|
The minimum voting window is the minimum amount of time that must pass in order
|
||||||
|
for a proposal to potentially pass, and it may be set to 0. The maximum voting
|
||||||
|
window is the maximum time that a proposal may be voted on before it is closed.
|
||||||
|
Both of these values must be less than a chain-wide max voting window parameter.
|
||||||
|
|
||||||
|
### Threshold decision policy
|
||||||
|
|
||||||
|
A threshold decision policy defines a threshold of yes votes (based on a tally
|
||||||
|
of voter weights) that must be achieved in order for a proposal to pass. For
|
||||||
|
this decision policy, abstain and veto are simply treated as no's.
|
||||||
|
|
||||||
|
## Proposal
|
||||||
|
|
||||||
|
Any member of a group can submit a proposal for a group account to decide upon.
|
||||||
|
A proposal consists of a set of messages that will be executed if the proposal
|
||||||
|
passes as well as any metadata associated with the proposal.
|
||||||
|
|
||||||
|
## Voting
|
||||||
|
|
||||||
|
There are four choices to choose while voting - yes, no, abstain and veto. Not
|
||||||
|
all decision policies will support them. Votes can contain some optional metadata.
|
||||||
|
During the voting window, accounts that have already voted may change their vote.
|
||||||
|
In the current implementation, the voting window begins as soon as a proposal
|
||||||
|
is submitted.
|
||||||
|
|
||||||
|
## Executing Proposals
|
||||||
|
|
||||||
|
Proposals will not be automatically executed by the chain in this current design,
|
||||||
|
but rather a user must submit a `Msg/Exec` transaction to attempt to execute the
|
||||||
|
proposal based on the current votes and decision policy.
|
||||||
|
It's also possible to try to execute a proposal immediately on creation or on
|
||||||
|
new votes using the `Exec` field of `Msg/CreateProposal` and `Msg/Vote` requests.
|
||||||
|
In the former case, proposers signatures are considered as yes votes.
|
||||||
|
For now, if the proposal can't be executed, it'll still be opened for new votes and
|
||||||
|
could be executed later on.
|
||||||
|
|
||||||
|
### Changing Group Membership
|
||||||
|
|
||||||
|
In the current implementation, changing a group's membership (adding or removing members or changing their weight)
|
||||||
|
will cause all existing proposals for group accounts linked to this group
|
||||||
|
to be invalidated. They will simply fail if someone calls `Msg/Exec` and will
|
||||||
|
eventually be garbage collected.
|
|
@ -0,0 +1,103 @@
|
||||||
|
<!--
|
||||||
|
order: 2
|
||||||
|
-->
|
||||||
|
|
||||||
|
# State
|
||||||
|
|
||||||
|
The `group` module uses the `orm` package which provides table storage with support for
|
||||||
|
primary keys and secondary indexes. `orm` also defines `Sequence` which is a persistent unique key generator based on a counter that can be used along with `Table`s.
|
||||||
|
|
||||||
|
Here's the list of tables and associated sequences and indexes stored as part of the `group` module.
|
||||||
|
|
||||||
|
## Group Table
|
||||||
|
|
||||||
|
The `groupTable` stores `GroupInfo`: `0x0 | BigEndian(GroupId) -> ProtocolBuffer(GroupInfo)`.
|
||||||
|
|
||||||
|
### groupSeq
|
||||||
|
|
||||||
|
The value of `groupSeq` is incremented when creating a new group and corresponds to the new `GroupId`: `0x1 | 0x1 -> BigEndian`.
|
||||||
|
|
||||||
|
The second `0x1` corresponds to the ORM `sequenceStorageKey`.
|
||||||
|
|
||||||
|
### groupByAdminIndex
|
||||||
|
|
||||||
|
`groupByAdminIndex` allows to retrieve groups by admin address:
|
||||||
|
`0x2 | len([]byte(group.Admin)) | []byte(group.Admin) | BigEndian(GroupId) -> []byte()`.
|
||||||
|
|
||||||
|
## Group Member Table
|
||||||
|
|
||||||
|
The `groupMemberTable` stores `GroupMember`s: `0x10 | BigEndian(GroupId) | []byte(member.Address) -> ProtocolBuffer(GroupMember)`.
|
||||||
|
|
||||||
|
The `groupMemberTable` is a primary key table and its `PrimaryKey` is given by
|
||||||
|
`BigEndian(GroupId) | []byte(member.Address)` which is used by the following indexes.
|
||||||
|
|
||||||
|
### groupMemberByGroupIndex
|
||||||
|
|
||||||
|
`groupMemberByGroupIndex` allows to retrieve group members by group id:
|
||||||
|
`0x11 | BigEndian(GroupId) | PrimaryKey -> []byte()`.
|
||||||
|
|
||||||
|
### groupMemberByMemberIndex
|
||||||
|
|
||||||
|
`groupMemberByMemberIndex` allows to retrieve group members by member address:
|
||||||
|
`0x12 | len([]byte(member.Address)) | []byte(member.Address) | PrimaryKey -> []byte()`.
|
||||||
|
|
||||||
|
## Group Account Table
|
||||||
|
|
||||||
|
The `groupAccountTable` stores `GroupAccountInfo`: `0x20 | len([]byte(Address)) | []byte(Address) -> ProtocolBuffer(GroupAccountInfo)`.
|
||||||
|
|
||||||
|
The `groupAccountTable` is a primary key table and its `PrimaryKey` is given by
|
||||||
|
`len([]byte(Address)) | []byte(Address)` which is used by the following indexes.
|
||||||
|
|
||||||
|
### groupAccountSeq
|
||||||
|
|
||||||
|
The value of `groupAccountSeq` is incremented when creating a new group account and is used to generate the new group account `Address`:
|
||||||
|
`0x21 | 0x1 -> BigEndian`.
|
||||||
|
|
||||||
|
The second `0x1` corresponds to the ORM `sequenceStorageKey`.
|
||||||
|
|
||||||
|
### groupAccountByGroupIndex
|
||||||
|
|
||||||
|
`groupAccountByGroupIndex` allows to retrieve group accounts by group id:
|
||||||
|
`0x22 | BigEndian(GroupId) | PrimaryKey -> []byte()`.
|
||||||
|
|
||||||
|
### groupAccountByAdminIndex
|
||||||
|
|
||||||
|
`groupAccountByAdminIndex` allows to retrieve group accounts by admin address:
|
||||||
|
`0x23 | len([]byte(Address)) | []byte(Address) | PrimaryKey -> []byte()`.
|
||||||
|
|
||||||
|
## Proposal Table
|
||||||
|
|
||||||
|
The `proposalTable` stores `Proposal`s: `0x30 | BigEndian(ProposalId) -> ProtocolBuffer(Proposal)`.
|
||||||
|
|
||||||
|
### proposalSeq
|
||||||
|
|
||||||
|
The value of `proposalSeq` is incremented when creating a new proposal and corresponds to the new `ProposalId`: `0x31 | 0x1 -> BigEndian`.
|
||||||
|
|
||||||
|
The second `0x1` corresponds to the ORM `sequenceStorageKey`.
|
||||||
|
|
||||||
|
### proposalByGroupAccountIndex
|
||||||
|
|
||||||
|
`proposalByGroupAccountIndex` allows to retrieve proposals by group account address:
|
||||||
|
`0x32 | len([]byte(account.Address)) | []byte(account.Address) | BigEndian(ProposalId) -> []byte()`.
|
||||||
|
|
||||||
|
### proposalByProposerIndex
|
||||||
|
|
||||||
|
`proposalByProposerIndex` allows to retrieve proposals by proposer address:
|
||||||
|
`0x33 | len([]byte(proposer.Address)) | []byte(proposer.Address) | BigEndian(ProposalId) -> []byte()`.
|
||||||
|
|
||||||
|
## Vote Table
|
||||||
|
|
||||||
|
The `voteTable` stores `Vote`s: `0x40 | BigEndian(ProposalId) | []byte(voter.Address) -> ProtocolBuffer(Vote)`.
|
||||||
|
|
||||||
|
The `voteTable` is a primary key table and its `PrimaryKey` is given by
|
||||||
|
`BigEndian(ProposalId) | []byte(voter.Address)` which is used by the following indexes.
|
||||||
|
|
||||||
|
### voteByProposalIndex
|
||||||
|
|
||||||
|
`voteByProposalIndex` allows to retrieve votes by proposal id:
|
||||||
|
`0x41 | BigEndian(ProposalId) | PrimaryKey -> []byte()`.
|
||||||
|
|
||||||
|
### voteByVoterIndex
|
||||||
|
|
||||||
|
`voteByVoterIndex` allows to retrieve votes by voter address:
|
||||||
|
`0x42 | len([]byte(voter.Address)) | []byte(voter.Address) | PrimaryKey -> []byte()`.
|
|
@ -0,0 +1,106 @@
|
||||||
|
<!--
|
||||||
|
order: 3
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Msg Service
|
||||||
|
|
||||||
|
## Msg/CreateGroup
|
||||||
|
|
||||||
|
A new group can be created with the `MsgCreateGroup`, which has an admin address, a list of members and some optional metadata bytes.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L54-L65
|
||||||
|
|
||||||
|
It's expecting to fail if metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupMembers
|
||||||
|
|
||||||
|
Group members can be updated with the `UpdateGroupMembers`.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L74-L86
|
||||||
|
|
||||||
|
In the list of `MemberUpdates`, an existing member can be removed by setting its weight to 0.
|
||||||
|
|
||||||
|
It's expecting to fail if the signer is not the admin of the group.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupAdmin
|
||||||
|
|
||||||
|
The `UpdateGroupAdmin` can be used to update a group admin.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L91-L102
|
||||||
|
|
||||||
|
It's expecting to fail if the signer is not the admin of the group.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupMetadata
|
||||||
|
|
||||||
|
The `UpdateGroupMetadata` can be used to update a group metadata.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L107-L118
|
||||||
|
|
||||||
|
It's expecting to fail if:
|
||||||
|
- new metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
- the signer is not the admin of the group.
|
||||||
|
|
||||||
|
## Msg/CreateGroupAccount
|
||||||
|
|
||||||
|
A new group account can be created with the `MsgCreateGroupAccount`, which has an admin address, a group id, a decision policy and some optional metadata bytes.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L121-L142
|
||||||
|
|
||||||
|
It's expecting to fail if metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupAccountAdmin
|
||||||
|
|
||||||
|
The `UpdateGroupAccountAdmin` can be used to update a group account admin.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L151-L162
|
||||||
|
|
||||||
|
It's expecting to fail if the signer is not the admin of the group account.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupAccountDecisionPolicy
|
||||||
|
|
||||||
|
The `UpdateGroupAccountDecisionPolicy` can be used to update a decision policy.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L167-L179
|
||||||
|
|
||||||
|
It's expecting to fail if the signer is not the admin of the group account.
|
||||||
|
|
||||||
|
## Msg/UpdateGroupAccountMetadata
|
||||||
|
|
||||||
|
The `UpdateGroupAccountMetadata` can be used to update a group account metadata.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L184-L195
|
||||||
|
|
||||||
|
It's expecting to fail if:
|
||||||
|
- new metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
- the signer is not the admin of the group.
|
||||||
|
|
||||||
|
## Msg/CreateProposal
|
||||||
|
|
||||||
|
A new group account can be created with the `MsgCreateProposal`, which has a group account address, a list of proposers addresses, a list of messages to execute if the proposal is accepted and some optional metadata bytes.
|
||||||
|
An optional `Exec` value can be provided to try to execute the proposal immediately after proposal creation. Proposers signatures are considered as yes votes in this case.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L218-L239
|
||||||
|
|
||||||
|
It's expecting to fail if metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
|
||||||
|
## Msg/Vote
|
||||||
|
|
||||||
|
A new vote can be created with the `MsgVote`, given a proposal id, a voter address, a choice (yes, no, veto or abstain) and some optional metadata bytes.
|
||||||
|
An optional `Exec` value can be provided to try to execute the proposal immediately after voting.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L248-L265
|
||||||
|
|
||||||
|
It's expecting to fail if metadata length is greater than some `MaxMetadataLength`.
|
||||||
|
|
||||||
|
## Msg/Exec
|
||||||
|
|
||||||
|
A proposal can be executed with the `MsgExec`.
|
||||||
|
|
||||||
|
+++ https://github.com/cosmos/cosmos-sdk/blob/6f58963e7f6ce820e9b33f02f06f7b96f6d2e347/proto/cosmos/group/v1beta1/tx.proto#L270-L278
|
||||||
|
|
||||||
|
The messages that are part of this proposal won't be executed if:
|
||||||
|
- the group has been modified before tally.
|
||||||
|
- the group account has been modified before tally.
|
||||||
|
- the proposal has not been accepted.
|
||||||
|
- the proposal status is not closed.
|
||||||
|
- the proposal has already been successfully executed.
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!--
|
||||||
|
order: 4
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Events
|
||||||
|
|
||||||
|
The group module emits the following events:
|
||||||
|
|
||||||
|
## EventCreateGroup
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|---------------------------------------|---------------|---------------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/CreateGroup |
|
||||||
|
| cosmos.group.v1beta1.EventCreateGroup | group_id | {groupId} |
|
||||||
|
|
||||||
|
## EventUpdateGroup
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|---------------------------------------|---------------|-----------------------------------------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/UpdateGroup{Admin\|Metadata\|Members} |
|
||||||
|
| cosmos.group.v1beta1.EventUpdateGroup | group_id | {groupId} |
|
||||||
|
|
||||||
|
## EventCreateGroupAccount
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|----------------------------------------------|---------------|----------------------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/CreateGroupAccount |
|
||||||
|
| cosmos.group.v1beta1.EventCreateGroupAccount | address | {groupAccountAddress} |
|
||||||
|
|
||||||
|
## EventUpdateGroupAccount
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|----------------------------------------------|---------------|-------------------------------------------------------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/UpdateGroupAccount{Admin\|Metadata\|DecisionPolicy} |
|
||||||
|
| cosmos.group.v1beta1.EventUpdateGroupAccount | address | {groupAccountAddress} |
|
||||||
|
|
||||||
|
## EventCreateProposal
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|------------------------------------------|---------------|------------------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/CreateProposal |
|
||||||
|
| cosmos.group.v1beta1.EventCreateProposal | proposal_id | {proposalId} |
|
||||||
|
|
||||||
|
## EventVote
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|--------------------------------|---------------|--------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/Vote |
|
||||||
|
| cosmos.group.v1beta1.EventVote | proposal_id | {proposalId} |
|
||||||
|
|
||||||
|
## EventExec
|
||||||
|
|
||||||
|
| Type | Attribute Key | Attribute Value |
|
||||||
|
|--------------------------------|---------------|--------------------------------|
|
||||||
|
| message | action | /cosmos.group.v1beta1.Msg/Exec |
|
||||||
|
| cosmos.group.v1beta1.EventExec | proposal_id | {proposalId} |
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,54 @@
|
||||||
|
<!--
|
||||||
|
order: 0
|
||||||
|
title: Group Overview
|
||||||
|
parent:
|
||||||
|
title: "group"
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Group Module
|
||||||
|
|
||||||
|
## Abstract
|
||||||
|
|
||||||
|
The following documents specify the group module.
|
||||||
|
|
||||||
|
This module allows the creation and management of on-chain multisig accounts and enables voting for message execution based on configurable decision policies.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
1. **[Concepts](01_concepts.md)**
|
||||||
|
- [Group](01_concepts.md#group)
|
||||||
|
- [Group Account](01_concepts.md#group-account)
|
||||||
|
- [Decision Policy](01_concepts.md#decision-policy)
|
||||||
|
- [Proposal](01_concepts.md#proposal)
|
||||||
|
- [Voting](01_concepts.md#voting)
|
||||||
|
- [Executing Proposals](01_concepts.md#executing-proposals)
|
||||||
|
2. **[State](02_state.md)**
|
||||||
|
- [Group Table](02_state.md#group-table)
|
||||||
|
- [Group Member Table](02_state.md#group-member-table)
|
||||||
|
- [Group Account Table](02_state.md#group-account-table)
|
||||||
|
- [Proposal](02_state.md#proposal-table)
|
||||||
|
- [Vote Table](02_state.md#vote-table)
|
||||||
|
3. **[Msg Service](03_messages.md)**
|
||||||
|
- [Msg/CreateGroup](03_messages.md#msgcreategroup)
|
||||||
|
- [Msg/UpdateGroupMembers](03_messages.md#msgupdategroupmembers)
|
||||||
|
- [Msg/UpdateGroupAdmin](03_messages.md#msgupdategroupadmin)
|
||||||
|
- [Msg/UpdateGroupMetadata](03_messages.md#msgupdategroupmetadata)
|
||||||
|
- [Msg/CreateGroupAccount](03_messages.md#msgcreategroupaccount)
|
||||||
|
- [Msg/UpdateGroupAccountAdmin](03_messages.md#msgupdategroupaccountadmin)
|
||||||
|
- [Msg/UpdateGroupAccountDecisionPolicy](03_messages.md#msgupdategroupaccountdecisionpolicy)
|
||||||
|
- [Msg/UpdateGroupAccountMetadata](03_messages.md#msgupdategroupaccountmetadata)
|
||||||
|
- [Msg/CreateProposal](03_messages.md#msgcreateproposal)
|
||||||
|
- [Msg/Vote](03_messages.md#msgvote)
|
||||||
|
- [Msg/Exec](03_messages.md#msgexec)
|
||||||
|
4. **[Events](04_events.md)**
|
||||||
|
- [EventCreateGroup](04_events.md#eventcreategroup)
|
||||||
|
- [EventUpdateGroup](04_events.md#eventupdategroup)
|
||||||
|
- [EventCreateGroupAccount](04_events.md#eventcreategroupaccount)
|
||||||
|
- [EventUpdateGroupAccount](04_events.md#eventupdategroupaccount)
|
||||||
|
- [EventCreateProposal](04_events.md#eventcreateproposal)
|
||||||
|
- [EventVote](04_events.md#eventvote)
|
||||||
|
- [EventExec](04_events.md#eventexec)
|
||||||
|
5. **[Client](05_client.md)**
|
||||||
|
- [CLI](05_client.md#cli)
|
||||||
|
- [gRPC](05_client.md#grpc)
|
||||||
|
- [REST](05_client.md#rest)
|
Loading…
Reference in New Issue