gov

Abstract

This paper specifies the Governance module of the Cosmos SDK, which was first described in the Cosmos Whitepaper in June 2016.

The module enables Cosmos SDK based blockchain to support an on-chain governance system. In this system, holders of the native staking token of the chain can vote on proposals on a 1 token 1 vote basis. Next is a list of features the module currently supports:

  • Proposal submission: Users can submit proposals with a deposit. Once the minimum deposit is reached, the proposal enters voting period. The minimum deposit can be reached by collecting deposits from different users (including proposer) within deposit period.

  • Vote: Participants can vote on proposals that reached MinDeposit and entered voting period.

  • Inheritance and penalties: Delegators inherit their validator's vote if they don't vote themselves.

  • Claiming deposit: Users that deposited on proposals can recover their deposits if the proposal was accepted or rejected. If the proposal was vetoed, or never entered voting period (minimum deposit not reached within deposit period), the deposit is burned.

Contents

The following specification uses ATOM as the native staking token. The module can be adapted to any Proof-Of-Stake blockchain by replacing ATOM with the native staking token of the chain.

Concepts

Disclaimer: This is work in progress. Mechanisms are susceptible to change.

The governance process is divided in a few steps that are outlined below:

  • Proposal submission: Proposal is submitted to the blockchain with a deposit.

  • Vote: Once deposit reaches a certain value (MinDeposit), proposal is confirmed and vote opens. Bonded Atom holders can then send TxGovVote transactions to vote on the proposal.

  • Execution After a period of time, the votes are tallied and depending on the result, the messages in the proposal will be executed.

Proposal submission

Right to submit a proposal

Every account can submit proposals by sending a MsgSubmitProposal transaction. Once a proposal is submitted, it is identified by its unique proposalID.

Proposal Messages

A proposal includes an array of sdk.Msgs which are executed automatically if the proposal passes. The messages are executed by the governance ModuleAccount itself. Modules such as x/upgrade, that want to allow certain messages to be executed by governance only should add a whitelist within the respective msg server, granting the governance module the right to execute the message once a quorum has been reached. The governance module uses the MsgServiceRouter to check that these messages are correctly constructed and have a respective path to execute on but do not perform a full validity check.

Deposit

To prevent spam, proposals must be submitted with a deposit in the coins defined by the MinDeposit param.

When a proposal is submitted, it has to be accompanied with a deposit that must be strictly positive, but can be inferior to MinDeposit. The submitter doesn't need to pay for the entire deposit on their own. The newly created proposal is stored in an inactive proposal queue and stays there until its deposit passes the MinDeposit. Other token holders can increase the proposal's deposit by sending a Deposit transaction. If a proposal doesn't pass the MinDeposit before the deposit end time (the time when deposits are no longer accepted), the proposal will be destroyed: the proposal will be removed from state and the deposit will be burned (see x/gov EndBlocker). When a proposal deposit passes the MinDeposit threshold (even during the proposal submission) before the deposit end time, the proposal will be moved into the active proposal queue and the voting period will begin.

The deposit is kept in escrow and held by the governance ModuleAccount until the proposal is finalized (passed or rejected).

Deposit refund and burn

When a proposal is finalized, the coins from the deposit are either refunded or burned according to the final tally of the proposal:

  • If the proposal is approved or rejected but not vetoed, each deposit will be automatically refunded to its respective depositor (transferred from the governance ModuleAccount).

  • When the proposal is vetoed with greater than 1/3, deposits will be burned from the governance ModuleAccount and the proposal information along with its deposit information will be removed from state.

  • All refunded or burned deposits are removed from the state. Events are issued when burning or refunding a deposit.

Vote

Participants

Participants are users that have the right to vote on proposals. On the Cosmos Hub, participants are bonded Atom holders. Unbonded Atom holders and other users do not get the right to participate in governance. However, they can submit and deposit on proposals.

Note that when participants have bonded and unbonded Atoms, their voting power is calculated from their bonded Atom holdings only.

Voting period

Once a proposal reaches MinDeposit, it immediately enters Voting period. We define Voting period as the interval between the moment the vote opens and the moment the vote closes. The initial value of Voting period is 2 weeks.

Option set

The option set of a proposal refers to the set of choices a participant can choose from when casting its vote.

The initial option set includes the following options:

  • Yes

  • No

  • NoWithVeto

  • Abstain

NoWithVeto counts as No but also adds a Veto vote. Abstain option allows voters to signal that they do not intend to vote in favor or against the proposal but accept the result of the vote.

Note: from the UI, for urgent proposals we should maybe add a ‘Not Urgent’ option that casts a NoWithVeto vote.

Weighted Votes

ADR-037 introduces the weighted vote feature which allows a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No.

Often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Currently, it is not possible for them to do "passthrough voting" and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll.

To represent weighted vote on chain, we use the following Protobuf message.

proto/cosmos/gov/v1beta1/gov.proto

// WeightedVoteOption defines a unit of vote for vote split.
//
// Since: cosmos-sdk 0.43
message WeightedVoteOption {
  // option defines the valid vote options, it must not contain duplicate vote options.
  VoteOption option = 1;

  // weight is the vote weight associated with the vote option. 
  string     weight = 2 [
    (cosmos_proto.scalar)  = "cosmos.Dec",
    (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
    (gogoproto.nullable)   = false
  ];
}

See full example on GitHubproto/cosmos/gov/v1beta1/gov.proto

// Vote defines a vote on a governance proposal.
// A Vote consists of a proposal ID, the voter, and the vote option.
message Vote {
  option (gogoproto.goproto_stringer) = false;
  option (gogoproto.equal)            = false;

  // proposal_id defines the unique id of the proposal.
  uint64 proposal_id = 1 [(gogoproto.jsontag) = "id", (amino.field_name) = "id", (amino.dont_omitempty) = true];
  
  // voter is the voter address of the proposal.
  string voter       = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
  // Deprecated: Prefer to use `options` instead. This field is set in queries
  // if and only if `len(options) == 1` and that option has weight 1. In all
  // other cases, this field will default to VOTE_OPTION_UNSPECIFIED.
  VoteOption option = 3 [deprecated = true];
  
  // options is the weighted vote options.
  //
  // Since: cosmos-sdk 0.43
  repeated WeightedVoteOption options = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}

See full example on GitHub

For a weighted vote to be valid, the options field must not contain duplicate vote options, and the sum of weights of all options must be equal to 1.

Quorum

Quorum is defined as the minimum percentage of voting power that needs to be cast on a proposal for the result to be valid.

Expedited Proposals

A proposal can be expedited, making the proposal use shorter voting duration and a higher tally threshold by its default. If an expedited proposal fails to meet the threshold within the scope of shorter voting duration, the expedited proposal is then converted to a regular proposal and restarts voting under regular voting conditions.

Threshold

Threshold is defined as the minimum proportion of Yes votes (excluding Abstain votes) for the proposal to be accepted.

Initially, the threshold is set at 50% of Yes votes, excluding Abstain votes. A possibility to veto exists if more than 1/3rd of all votes are NoWithVeto votes. Note, both of these values are derived from the TallyParams on-chain parameter, which is modifiable by governance. This means that proposals are accepted iff:

  • There exist bonded tokens.

  • Quorum has been achieved.

  • The proportion of Abstain votes is inferior to 1/1.

  • The proportion of NoWithVeto votes is inferior to 1/3, including Abstain votes.

  • The proportion of Yes votes, excluding Abstain votes, at the end of the voting period is superior to 1/2.

For expedited proposals, by default, the threshold is higher than with a normal proposal, namely, 66.7%.

Inheritance

If a delegator does not vote, it will inherit its validator vote.

  • If the delegator votes before its validator, it will not inherit from the validator's vote.

  • If the delegator votes after its validator, it will override its validator vote with its own. If the proposal is urgent, it is possible that the vote will close before delegators have a chance to react and override their validator's vote. This is not a problem, as proposals require more than 2/3rd of the total voting power to pass, when tallied at the end of the voting period. Because as little as 1/3 + 1 validation power could collude to censor transactions, non-collusion is already assumed for ranges exceeding this threshold.

Validator’s punishment for non-voting

At present, validators are not punished for failing to vote.

Governance address

Later, we may add permissioned keys that could only sign txs from certain modules. For the MVP, the Governance address will be the main validator address generated at account creation. This address corresponds to a different PrivKey than the CometBFT PrivKey which is responsible for signing consensus messages. Validators thus do not have to sign governance transactions with the sensitive CometBFT PrivKey.

Burnable Params

There are three parameters that define if the deposit of a proposal should be burned or returned to the depositors.

  • BurnVoteVeto burns the proposal deposit if the proposal gets vetoed.

  • BurnVoteQuorum burns the proposal deposit if the proposal deposit if the vote does not reach quorum.

  • BurnProposalDepositPrevote burns the proposal deposit if it does not enter the voting phase.

Note: These parameters are modifiable via governance.

State

Constitution

Constitution is found in the genesis state. It is a string field intended to be used to descibe the purpose of a particular blockchain, and its expected norms. A few examples of how the constitution field can be used:

  • define the purpose of the chain, laying a foundation for its future development

  • set expectations for delegators

  • set expectations for validators

  • define the chain's relationship to "meatspace" entities, like a foundation or corporation

Since this is more of a social feature than a technical feature, we'll now get into some items that may have been useful to have in a genesis constitution:

  • What limitations on governance exist, if any?

    • is it okay for the community to slash the wallet of a whale that they no longer feel that they want around? (viz: Juno Proposal 4 and 16)

    • can governance "socially slash" a validator who is using unapproved MEV? (viz: commonwealth.im/osmosis)

    • In the event of an economic emergency, what should validators do?

      • Terra crash of May, 2022, saw validators choose to run a new binary with code that had not been approved by governance, because the governance token had been inflated to nothing.

  • What is the purpose of the chain, specifically?

    • best example of this is the Cosmos hub, where different founding groups, have different interpertations of the purpose of the network.

This genesis entry, "constitution" hasn't been designed for existing chains, who should likely just ratify a constitution using their governance system. Instead, this is for new chains. It will allow for validators to have a much clearer idea of purpose and the expecations placed on them while operating thier nodes. Likewise, for community members, the constitution will give them some idea of what to expect from both the "chain team" and the validators, respectively.

This constitution is designed to be immutable, and placed only in genesis, though that could change over time by a pull request to the cosmos-sdk that allows for the constitution to be changed by governance. Communities whishing to make amendments to their original constitution should use the governance mechanism and a "signaling proposal" to do exactly that.

Ideal use scenario for a cosmos chain constitution

As a chain developer, you decide that you'd like to provide clarity to your key user groups:

  • validators

  • token holders

  • developers (yourself)

You use the constitution to immutably store some Markdown in genesis, so that when difficult questions come up, the constutituon can provide guidance to the community.

Proposals

Proposal objects are used to tally votes and generally track the proposal's state. They contain an array of arbitrary sdk.Msg's which the governance module will attempt to resolve and then execute if the proposal passes. Proposal's are identified by a unique id and contains a series of timestamps: submit_time, deposit_end_time, voting_start_time, voting_end_time which track the lifecycle of a proposal

proto/cosmos/gov/v1/gov.proto

// Proposal defines the core field members of a governance proposal.
message Proposal {
  // id defines the unique id of the proposal.
  uint64   id                           = 1;

  // messages are the arbitrary messages to be executed if the proposal passes.
  repeated google.protobuf.Any messages = 2;

  // status defines the proposal status.
  ProposalStatus               status   = 3;

  // final_tally_result is the final tally result of the proposal. When
  // querying a proposal via gRPC, this field is not populated until the
  // proposal's voting period has ended.
  TallyResult               final_tally_result        = 4;
  
  // submit_time is the time of proposal submission.
  google.protobuf.Timestamp submit_time               = 5 [(gogoproto.stdtime) = true];
  
  // deposit_end_time is the end time for deposition.
  google.protobuf.Timestamp deposit_end_time          = 6 [(gogoproto.stdtime) = true];
 
  // total_deposit is the total deposit on the proposal.
  repeated cosmos.base.v1beta1.Coin total_deposit     = 7 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
  
  // voting_start_time is the starting time to vote on a proposal.
  google.protobuf.Timestamp         voting_start_time = 8 [(gogoproto.stdtime) = true];
 
  // voting_end_time is the end time of voting on a proposal.
  google.protobuf.Timestamp         voting_end_time   = 9 [(gogoproto.stdtime) = true];

  // metadata is any arbitrary metadata attached to the proposal.
  string metadata = 10;

  // title is the title of the proposal
  //
  // Since: cosmos-sdk 0.47
  string title = 11;

  // summary is a short summary of the proposal
  //
  // Since: cosmos-sdk 0.47
  string summary = 12;

  // Proposer is the address of the proposal sumbitter
  //
  // Since: cosmos-sdk 0.47
  string proposer = 13 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

See full example on GitHub

A proposal will generally require more than just a set of messages to explain its purpose but need some greater justification and allow a means for interested participants to discuss and debate the proposal. In most cases, it is encouraged to have an off-chain system that supports the on-chain governance process. To accommodate for this, a proposal contains a special metadata field, a string, which can be used to add context to the proposal. The metadata field allows custom use for networks, however, it is expected that the field contains a URL or some form of CID using a system such as IPFS. To support the case of interoperability across networks, the SDK recommends that the metadata represents the following JSON template:

{
  "title": "...",
  "description": "...",
  "forum": "...", // a link to the discussion platform (i.e. Discord)
  "other": "..." // any extra data that doesn't correspond to the other fields
}

This makes it far easier for clients to support multiple networks.

The metadata has a maximum length that is chosen by the app developer, and passed into the gov keeper as a config. The default maximum length in the SDK is 255 characters.

Writing a module that uses governance

There are many aspects of a chain, or of the individual modules that you may want to use governance to perform such as changing various parameters. This is very simple to do. First, write out your message types and MsgServer implementation. Add an authority field to the keeper which will be populated in the constructor with the governance module account: govKeeper.GetGovernanceAccount().GetAddress(). Then for the methods in the msg_server.go, perform a check on the message that the signer matches authority. This will prevent any user from executing that message.

Parameters and base types

Parameters define the rules according to which votes are run. There can only be one active parameter set at any given time. If governance wants to change a parameter set, either to modify a value or add/remove a parameter field, a new parameter set has to be created and the previous one rendered inactive.

DepositParams

proto/cosmos/gov/v1/gov.proto

// DepositParams defines the params for deposits on governance proposals.
message DepositParams {
  // Minimum deposit for a proposal to enter voting period.
  repeated cosmos.base.v1beta1.Coin min_deposit = 1
      [(gogoproto.nullable) = false, (gogoproto.jsontag) = "min_deposit,omitempty"];

  // Maximum period for Atom holders to deposit on a proposal. Initial value: 2
  // months.
  google.protobuf.Duration max_deposit_period = 2
      [(gogoproto.stdduration) = true, (gogoproto.jsontag) = "max_deposit_period,omitempty"];
}

See full example on GitHub

VotingParams

proto/cosmos/gov/v1/gov.proto

// VotingParams defines the params for voting on governance proposals.
message VotingParams {
  // Duration of the voting period.
  google.protobuf.Duration voting_period = 1 [(gogoproto.stdduration) = true];
}

See full example on GitHub

TallyParams

proto/cosmos/gov/v1/gov.proto

// TallyParams defines the params for tallying votes on governance proposals.
message TallyParams {
  // Minimum percentage of total stake needed to vote for a result to be
  // considered valid.
  string quorum = 1 [(cosmos_proto.scalar) = "cosmos.Dec"];

  // Minimum proportion of Yes votes for proposal to pass. Default value: 0.5.
  string threshold = 2 [(cosmos_proto.scalar) = "cosmos.Dec"];

  // Minimum value of Veto votes to Total votes ratio for proposal to be
  // vetoed. Default value: 1/3.
  string veto_threshold = 3 [(cosmos_proto.scalar) = "cosmos.Dec"];
}

See full example on GitHub

Parameters are stored in a global GlobalParams KVStore.

Additionally, we introduce some basic types:

type Vote byte

const (
    VoteYes         = 0x1
    VoteNo          = 0x2
    VoteNoWithVeto  = 0x3
    VoteAbstain     = 0x4
)

type ProposalType  string

const (
    ProposalTypePlainText       = "Text"
    ProposalTypeSoftwareUpgrade = "SoftwareUpgrade"
)

type ProposalStatus byte


const (
    StatusNil           ProposalStatus = 0x00
    StatusDepositPeriod ProposalStatus = 0x01  // Proposal is submitted. Participants can deposit on it but not vote
    StatusVotingPeriod  ProposalStatus = 0x02  // MinDeposit is reached, participants can vote
    StatusPassed        ProposalStatus = 0x03  // Proposal passed and successfully executed
    StatusRejected      ProposalStatus = 0x04  // Proposal has been rejected
    StatusFailed        ProposalStatus = 0x05  // Proposal passed but failed execution
)

Deposit

proto/cosmos/gov/v1/gov.proto

// Deposit defines an amount deposited by an account address to an active
// proposal.
message Deposit {
  // proposal_id defines the unique id of the proposal.
  uint64   proposal_id                     = 1;
  
  // depositor defines the deposit addresses from the proposals.
  string   depositor                       = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
  
  // amount to be deposited by depositor.
  repeated cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}

See full example on GitHub

ValidatorGovInfo

This type is used in a temp map when tallying

  type ValidatorGovInfo struct {
    Minus     sdk.Dec
    Vote      Vote
  }

Stores

note

Stores are KVStores in the multi-store. The key to find the store is the first parameter in the list

We will use one KVStore Governance to store four mappings:

  • A mapping from proposalID|'proposal' to Proposal.

  • A mapping from proposalID|'addresses'|address to Vote. This mapping allows us to query all addresses that voted on the proposal along with their vote by doing a range query on proposalID:addresses.

  • A mapping from ParamsKey|'Params' to Params. This map allows to query all x/gov params.

  • A mapping from VotingPeriodProposalKeyPrefix|proposalID to a single byte. This allows us to know if a proposal is in the voting period or not with very low gas cost.

For pseudocode purposes, here are the two function we will use to read or write in stores:

  • load(StoreKey, Key): Retrieve item stored at key Key in store found at key StoreKey in the multistore

  • store(StoreKey, Key, value): Write value Value at key Key in store found at key StoreKey in the multistore

Proposal Processing Queue

Store:

  • ProposalProcessingQueue: A queue queue[proposalID] containing all the ProposalIDs of proposals that reached MinDeposit. During each EndBlock, all the proposals that have reached the end of their voting period are processed. To process a finished proposal, the application tallies the votes, computes the votes of each validator and checks if every validator in the validator set has voted. If the proposal is accepted, deposits are refunded. Finally, the proposal content Handler is executed.

And the pseudocode for the ProposalProcessingQueue:

  in EndBlock do

    for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
      proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key

      validators = Keeper.getAllValidators()
      tmpValMap := map(sdk.AccAddress)ValidatorGovInfo

      // Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
      for each validator in validators
        tmpValMap(validator.OperatorAddr).Minus = 0

      // Tally
      voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
      for each (voterAddress, vote) in voterIterator
        delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter

        for each delegation in delegations
          // make sure delegation.Shares does NOT include shares being unbonded
          tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
          proposal.updateTally(vote, delegation.Shares)

        _, isVal = stakingKeeper.getValidator(voterAddress)
        if (isVal)
          tmpValMap(voterAddress).Vote = vote

      tallyingParam = load(GlobalParams, 'TallyingParam')

      // Update tally if validator voted
      for each validator in validators
        if tmpValMap(validator).HasVoted
          proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))



      // Check if proposal is accepted or rejected
      totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
      if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain  < tallyingParam.Veto)
        //  proposal was accepted at the end of the voting period
        //  refund deposits (non-voters already punished)
        for each (amount, depositor) in proposal.Deposits
          depositor.AtomBalance += amount

        stateWriter, err := proposal.Handler()
        if err != nil
            // proposal passed but failed during state execution
            proposal.CurrentStatus = ProposalStatusFailed
         else
            // proposal pass and state is persisted
            proposal.CurrentStatus = ProposalStatusAccepted
            stateWriter.save()
      else
        // proposal was rejected
        proposal.CurrentStatus = ProposalStatusRejected

      store(Governance, <proposalID|'proposal'>, proposal)

Legacy Proposal

danger

Legacy proposals are deprecated. Use the new proposal flow by granting the governance module the right to execute the message.

A legacy proposal is the old implementation of governance proposal. Contrary to proposal that can contain any messages, a legacy proposal allows to submit a set of pre-defined proposals. These proposals are defined by their types and handled by handlers that are registered in the gov v1beta1 router.

More information on how to submit proposals in the client section.

Messages

Proposal Submission

Proposals can be submitted by any account via a MsgSubmitProposal transaction.

proto/cosmos/gov/v1/tx.proto

// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary
// proposal Content.
message MsgSubmitProposal {
  option (cosmos.msg.v1.signer) = "proposer";
  option (amino.name)           = "cosmos-sdk/v1/MsgSubmitProposal";

  // messages are the arbitrary messages to be executed if proposal passes.
  repeated google.protobuf.Any messages             = 1;

  // initial_deposit is the deposit value that must be paid at proposal submission.
  repeated cosmos.base.v1beta1.Coin initial_deposit = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
  
  // proposer is the account address of the proposer.
  string                            proposer        = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
  
  // metadata is any arbitrary metadata attached to the proposal.
  string metadata = 4;

  // title is the title of the proposal.
  //
  // Since: cosmos-sdk 0.47
  string title = 5;

  // summary is the summary of the proposal
  //
  // Since: cosmos-sdk 0.47
  string summary = 6;
}

See full example on GitHub

All sdk.Msgs passed into the messages field of a MsgSubmitProposal message must be registered in the app's MsgServiceRouter. Each of these messages must have one signer, namely the gov module account. And finally, the metadata length must not be larger than the maxMetadataLen config passed into the gov keeper. The initialDeposit must be strictly positive and conform to the accepted denom of the MinDeposit param.

State modifications:

  • Generate new proposalID

  • Create new Proposal

  • Initialise Proposal's attributes

  • Decrease balance of sender by InitialDeposit

  • If MinDeposit is reached:

    • Push proposalID in ProposalProcessingQueue

  • Transfer InitialDeposit from the Proposer to the governance ModuleAccount

Deposit

Once a proposal is submitted, if Proposal.TotalDeposit < ActiveParam.MinDeposit, Atom holders can send MsgDeposit transactions to increase the proposal's deposit.

A deposit is accepted iff:

  • The proposal exists

  • The proposal is not in the voting period

  • The deposited coins are conform to the accepted denom from the MinDeposit param

proto/cosmos/gov/v1/tx.proto

// MsgDeposit defines a message to submit a deposit to an existing proposal.
message MsgDeposit {
  option (cosmos.msg.v1.signer) = "depositor";
  option (amino.name)           = "cosmos-sdk/v1/MsgDeposit";

  // proposal_id defines the unique id of the proposal.
  uint64   proposal_id                     = 1 [(gogoproto.jsontag) = "proposal_id", (amino.dont_omitempty) = true];
  
  // depositor defines the deposit addresses from the proposals.
  string   depositor                       = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
  
  // amount to be deposited by depositor.
  repeated cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}

See full example on GitHub

State modifications:

  • Decrease balance of sender by deposit

  • Add deposit of sender in proposal.Deposits

  • Increase proposal.TotalDeposit by sender's deposit

  • If MinDeposit is reached:

    • Push proposalID in ProposalProcessingQueueEnd

  • Transfer Deposit from the proposer to the governance ModuleAccount

Vote

Once ActiveParam.MinDeposit is reached, voting period starts. From there, bonded Atom holders are able to send MsgVote transactions to cast their vote on the proposal.

proto/cosmos/gov/v1/tx.proto

// MsgVote defines a message to cast a vote.
message MsgVote {
  option (cosmos.msg.v1.signer) = "voter";
  option (amino.name)           = "cosmos-sdk/v1/MsgVote";

  // proposal_id defines the unique id of the proposal.
  uint64     proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (amino.dont_omitempty) = true];
  
  // voter is the voter address for the proposal.
  string     voter       = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
  
  // option defines the vote option.
  VoteOption option      = 3;
  
  // metadata is any arbitrary metadata attached to the Vote.
  string     metadata    = 4;
}

See full example on GitHub

State modifications:

  • Record Vote of sender

note

Gas cost for this message has to take into account the future tallying of the vote in EndBlocker.

Events

The governance module emits the following events:

EndBlocker

TypeAttribute KeyAttribute Value

inactive_proposal

proposal_id

{proposalID}

inactive_proposal

proposal_result

{proposalResult}

active_proposal

proposal_id

{proposalID}

active_proposal

proposal_result

{proposalResult}

Handlers

MsgSubmitProposal

TypeAttribute KeyAttribute Value

submit_proposal

proposal_id

{proposalID}

submit_proposal [0]

voting_period_start

{proposalID}

proposal_deposit

amount

{depositAmount}

proposal_deposit

proposal_id

{proposalID}

message

module

governance

message

action

submit_proposal

message

sender

{senderAddress}

  • [0] Event only emitted if the voting period starts during the submission.

MsgVote

TypeAttribute KeyAttribute Value

proposal_vote

option

{voteOption}

proposal_vote

proposal_id

{proposalID}

message

module

governance

message

action

vote

message

sender

{senderAddress}

MsgVoteWeighted

TypeAttribute KeyAttribute Value

proposal_vote

option

{weightedVoteOptions}

proposal_vote

proposal_id

{proposalID}

message

module

governance

message

action

vote

message

sender

{senderAddress}

MsgDeposit

TypeAttribute KeyAttribute Value

proposal_deposit

amount

{depositAmount}

proposal_deposit

proposal_id

{proposalID}

proposal_deposit [0]

voting_period_start

{proposalID}

message

module

governance

message

action

deposit

message

sender

{senderAddress}

  • [0] Event only emitted if the voting period starts during the submission.

Parameters

The governance module contains the following parameters:

KeyTypeExample

min_deposit

array (coins)

[{"denom":"uatom","amount":"10000000"}]

max_deposit_period

string (time ns)

"172800000000000" (17280s)

voting_period

string (time ns)

"172800000000000" (17280s)

quorum

string (dec)

"0.334000000000000000"

threshold

string (dec)

"0.500000000000000000"

veto

string (dec)

"0.334000000000000000"

expedited_threshold

string (time ns)

"0.667000000000000000"

expedited_voting_period

string (time ns)

"86400000000000" (8600s)

expedited_min_deposit

array (coins)

[{"denom":"uatom","amount":"50000000"}]

burn_proposal_deposit_prevote

bool

false

burn_vote_quorum

bool

false

burn_vote_veto

bool

true

min_initial_deposit_ratio

string

"0.1"

NOTE: The governance module contains parameters that are objects unlike other modules. If only a subset of parameters are desired to be changed, only they need to be included and not the entire parameter object structure.

Client

CLI

A user can query and interact with the gov module using the CLI.

Query

The query commands allow users to query gov state.

simd query gov --help

deposit

The deposit command allows users to query a deposit for a given proposal from a given depositor.

simd query gov deposit [proposal-id] [depositer-addr] [flags]

Example:

simd query gov deposit 1 cosmos1..

Example Output:

amount:
- amount: "100"
  denom: stake
depositor: cosmos1..
proposal_id: "1"

deposits

The deposits command allows users to query all deposits for a given proposal.

simd query gov deposits [proposal-id] [flags]

Example:

simd query gov deposits 1

Example Output:

deposits:
- amount:
  - amount: "100"
    denom: stake
  depositor: cosmos1..
  proposal_id: "1"
pagination:
  next_key: null
  total: "0"

param

The param command allows users to query a given parameter for the gov module.

simd query gov param [param-type] [flags]

Example:

simd query gov param voting

Example Output:

voting_period: "172800000000000"

params

The params command allows users to query all parameters for the gov module.

simd query gov params [flags]

Example:

simd query gov params

Example Output:

deposit_params:
  max_deposit_period: 172800s
  min_deposit:
  - amount: "10000000"
    denom: stake
params:
  expedited_min_deposit:
  - amount: "50000000"
    denom: stake
  expedited_threshold: "0.670000000000000000"
  expedited_voting_period: 86400s
  max_deposit_period: 172800s
  min_deposit:
  - amount: "10000000"
    denom: stake
  min_initial_deposit_ratio: "0.000000000000000000"
  proposal_cancel_burn_rate: "0.500000000000000000"
  quorum: "0.334000000000000000"
  threshold: "0.500000000000000000"
  veto_threshold: "0.334000000000000000"
  voting_period: 172800s
tally_params:
  quorum: "0.334000000000000000"
  threshold: "0.500000000000000000"
  veto_threshold: "0.334000000000000000"
voting_params:
  voting_period: 172800s

proposal

The proposal command allows users to query a given proposal.

simd query gov proposal [proposal-id] [flags]

Example:

simd query gov proposal 1

Example Output:

deposit_end_time: "2022-03-30T11:50:20.819676256Z"
final_tally_result:
  abstain_count: "0"
  no_count: "0"
  no_with_veto_count: "0"
  yes_count: "0"
id: "1"
messages:
- '@type': /cosmos.bank.v1beta1.MsgSend
  amount:
  - amount: "10"
    denom: stake
  from_address: cosmos1..
  to_address: cosmos1..
metadata: AQ==
status: PROPOSAL_STATUS_DEPOSIT_PERIOD
submit_time: "2022-03-28T11:50:20.819676256Z"
total_deposit:
- amount: "10"
  denom: stake
voting_end_time: null
voting_start_time: null

proposals

The proposals command allows users to query all proposals with optional filters.

simd query gov proposals [flags]

Example:

simd query gov proposals

Example Output:

pagination:
  next_key: null
  total: "0"
proposals:
- deposit_end_time: "2022-03-30T11:50:20.819676256Z"
  final_tally_result:
    abstain_count: "0"
    no_count: "0"
    no_with_veto_count: "0"
    yes_count: "0"
  id: "1"
  messages:
  - '@type': /cosmos.bank.v1beta1.MsgSend
    amount:
    - amount: "10"
      denom: stake
    from_address: cosmos1..
    to_address: cosmos1..
  metadata: AQ==
  status: PROPOSAL_STATUS_DEPOSIT_PERIOD
  submit_time: "2022-03-28T11:50:20.819676256Z"
  total_deposit:
  - amount: "10"
    denom: stake
  voting_end_time: null
  voting_start_time: null
- deposit_end_time: "2022-03-30T14:02:41.165025015Z"
  final_tally_result:
    abstain_count: "0"
    no_count: "0"
    no_with_veto_count: "0"
    yes_count: "0"
  id: "2"
  messages:
  - '@type': /cosmos.bank.v1beta1.MsgSend
    amount:
    - amount: "10"
      denom: stake
    from_address: cosmos1..
    to_address: cosmos1..
  metadata: AQ==
  status: PROPOSAL_STATUS_DEPOSIT_PERIOD
  submit_time: "2022-03-28T14:02:41.165025015Z"
  total_deposit:
  - amount: "10"
    denom: stake
  voting_end_time: null
  voting_start_time: null

proposer

The proposer command allows users to query the proposer for a given proposal.

simd query gov proposer [proposal-id] [flags]

Example:

simd query gov proposer 1

Example Output:

proposal_id: "1"
proposer: cosmos1..

tally

The tally command allows users to query the tally of a given proposal vote.

simd query gov tally [proposal-id] [flags]

Example:

simd query gov tally 1

Example Output:

abstain: "0"
"no": "0"
no_with_veto: "0"
"yes": "1"

vote

The vote command allows users to query a vote for a given proposal.

simd query gov vote [proposal-id] [voter-addr] [flags]

Example:

simd query gov vote 1 cosmos1..

Example Output:

option: VOTE_OPTION_YES
options:
- option: VOTE_OPTION_YES
  weight: "1.000000000000000000"
proposal_id: "1"
voter: cosmos1..

votes

The votes command allows users to query all votes for a given proposal.

simd query gov votes [proposal-id] [flags]

Example:

simd query gov votes 1

Example Output:

pagination:
  next_key: null
  total: "0"
votes:
- option: VOTE_OPTION_YES
  options:
  - option: VOTE_OPTION_YES
    weight: "1.000000000000000000"
  proposal_id: "1"
  voter: cosmos1..

Transactions

The tx commands allow users to interact with the gov module.

simd tx gov --help

deposit

The deposit command allows users to deposit tokens for a given proposal.

simd tx gov deposit [proposal-id] [deposit] [flags]

Example:

simd tx gov deposit 1 10000000stake --from cosmos1..

draft-proposal

The draft-proposal command allows users to draft any type of proposal. The command returns a draft_proposal.json, to be used by submit-proposal after being completed. The draft_metadata.json is meant to be uploaded to IPFS.

simd tx gov draft-proposal

submit-proposal

The submit-proposal command allows users to submit a governance proposal along with some messages and metadata. Messages, metadata and deposit are defined in a JSON file.

simd tx gov submit-proposal [path-to-proposal-json] [flags]

Example:

simd tx gov submit-proposal /path/to/proposal.json --from cosmos1..

where proposal.json contains:

{
  "messages": [
    {
      "@type": "/cosmos.bank.v1beta1.MsgSend",
      "from_address": "cosmos1...", // The gov module module address
      "to_address": "cosmos1...",
      "amount":[{"denom": "stake","amount": "10"}]
    }
  ],
  "metadata": "AQ==",
  "deposit": "10stake",
  "title": "Proposal Title",
  "summary": "Proposal Summary"
}

note

By default the metadata, summary and title are both limited by 255 characters, this can be overridden by the application developer.

tip

When metadata is not specified, the title is limited to 255 characters and the summary 40x the title length.

submit-legacy-proposal

The submit-legacy-proposal command allows users to submit a governance legacy proposal along with an initial deposit.

simd tx gov submit-legacy-proposal [command] [flags]

Example:

simd tx gov submit-legacy-proposal --title="Test Proposal" --description="testing" --type="Text" --deposit="100000000stake" --from cosmos1..

Example (param-change):

simd tx gov submit-legacy-proposal param-change proposal.json --from cosmos1..
{
  "title": "Test Proposal",
  "description": "testing, testing, 1, 2, 3",
  "changes": [
    {
      "subspace": "staking",
      "key": "MaxValidators",
      "value": 100
    }
  ],
  "deposit": "10000000stake"
}

cancel-proposal

Once proposal is canceled, from the deposits of proposal deposits * proposal_cancel_ratio will be burned or sent to ProposalCancelDest address , if ProposalCancelDest is empty then deposits will be burned. The remaining deposits will be sent to depositers.

simd tx gov cancel-proposal [proposal-id] [flags]

Example:

simd tx gov cancel-proposal 1 --from cosmos1...

vote

The vote command allows users to submit a vote for a given governance proposal.

simd tx gov vote [command] [flags]

Example:

simd tx gov vote 1 yes --from cosmos1..

weighted-vote

The weighted-vote command allows users to submit a weighted vote for a given governance proposal.

simd tx gov weighted-vote [proposal-id] [weighted-options] [flags]

Example:

simd tx gov weighted-vote 1 yes=0.5,no=0.5 --from cosmos1..

gRPC

A user can query the gov module using gRPC endpoints.

Proposal

The Proposal endpoint allows users to query a given proposal.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Proposal

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Proposal

Example Output:

{
  "proposal": {
    "proposalId": "1",
    "content": {"@type":"/cosmos.gov.v1beta1.TextProposal","description":"testing, testing, 1, 2, 3","title":"Test Proposal"},
    "status": "PROPOSAL_STATUS_VOTING_PERIOD",
    "finalTallyResult": {
      "yes": "0",
      "abstain": "0",
      "no": "0",
      "noWithVeto": "0"
    },
    "submitTime": "2021-09-16T19:40:08.712440474Z",
    "depositEndTime": "2021-09-18T19:40:08.712440474Z",
    "totalDeposit": [
      {
        "denom": "stake",
        "amount": "10000000"
      }
    ],
    "votingStartTime": "2021-09-16T19:40:08.712440474Z",
    "votingEndTime": "2021-09-18T19:40:08.712440474Z",
    "title": "Test Proposal",
    "summary": "testing, testing, 1, 2, 3"
  }
}

Using v1:

cosmos.gov.v1.Query/Proposal

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Proposal

Example Output:

{
  "proposal": {
    "id": "1",
    "messages": [
      {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."}
    ],
    "status": "PROPOSAL_STATUS_VOTING_PERIOD",
    "finalTallyResult": {
      "yesCount": "0",
      "abstainCount": "0",
      "noCount": "0",
      "noWithVetoCount": "0"
    },
    "submitTime": "2022-03-28T11:50:20.819676256Z",
    "depositEndTime": "2022-03-30T11:50:20.819676256Z",
    "totalDeposit": [
      {
        "denom": "stake",
        "amount": "10000000"
      }
    ],
    "votingStartTime": "2022-03-28T14:25:26.644857113Z",
    "votingEndTime": "2022-03-30T14:25:26.644857113Z",
    "metadata": "AQ==",
    "title": "Test Proposal",
    "summary": "testing, testing, 1, 2, 3"
  }
}

Proposals

The Proposals endpoint allows users to query all proposals with optional filters.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Proposals

Example:

grpcurl -plaintext \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Proposals

Example Output:

{
  "proposals": [
    {
      "proposalId": "1",
      "status": "PROPOSAL_STATUS_VOTING_PERIOD",
      "finalTallyResult": {
        "yes": "0",
        "abstain": "0",
        "no": "0",
        "noWithVeto": "0"
      },
      "submitTime": "2022-03-28T11:50:20.819676256Z",
      "depositEndTime": "2022-03-30T11:50:20.819676256Z",
      "totalDeposit": [
        {
          "denom": "stake",
          "amount": "10000000010"
        }
      ],
      "votingStartTime": "2022-03-28T14:25:26.644857113Z",
      "votingEndTime": "2022-03-30T14:25:26.644857113Z"
    },
    {
      "proposalId": "2",
      "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD",
      "finalTallyResult": {
        "yes": "0",
        "abstain": "0",
        "no": "0",
        "noWithVeto": "0"
      },
      "submitTime": "2022-03-28T14:02:41.165025015Z",
      "depositEndTime": "2022-03-30T14:02:41.165025015Z",
      "totalDeposit": [
        {
          "denom": "stake",
          "amount": "10"
        }
      ],
      "votingStartTime": "0001-01-01T00:00:00Z",
      "votingEndTime": "0001-01-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": "2"
  }
}

Using v1:

cosmos.gov.v1.Query/Proposals

Example:

grpcurl -plaintext \
    localhost:9090 \
    cosmos.gov.v1.Query/Proposals

Example Output:

{
  "proposals": [
    {
      "id": "1",
      "messages": [
        {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."}
      ],
      "status": "PROPOSAL_STATUS_VOTING_PERIOD",
      "finalTallyResult": {
        "yesCount": "0",
        "abstainCount": "0",
        "noCount": "0",
        "noWithVetoCount": "0"
      },
      "submitTime": "2022-03-28T11:50:20.819676256Z",
      "depositEndTime": "2022-03-30T11:50:20.819676256Z",
      "totalDeposit": [
        {
          "denom": "stake",
          "amount": "10000000010"
        }
      ],
      "votingStartTime": "2022-03-28T14:25:26.644857113Z",
      "votingEndTime": "2022-03-30T14:25:26.644857113Z",
      "metadata": "AQ==",
      "title": "Proposal Title",
      "summary": "Proposal Summary"
    },
    {
      "id": "2",
      "messages": [
        {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."}
      ],
      "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD",
      "finalTallyResult": {
        "yesCount": "0",
        "abstainCount": "0",
        "noCount": "0",
        "noWithVetoCount": "0"
      },
      "submitTime": "2022-03-28T14:02:41.165025015Z",
      "depositEndTime": "2022-03-30T14:02:41.165025015Z",
      "totalDeposit": [
        {
          "denom": "stake",
          "amount": "10"
        }
      ],
      "metadata": "AQ==",
      "title": "Proposal Title",
      "summary": "Proposal Summary"
    }
  ],
  "pagination": {
    "total": "2"
  }
}

Vote

The Vote endpoint allows users to query a vote for a given proposal.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Vote

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1","voter":"cosmos1.."}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Vote

Example Output:

{
  "vote": {
    "proposalId": "1",
    "voter": "cosmos1..",
    "option": "VOTE_OPTION_YES",
    "options": [
      {
        "option": "VOTE_OPTION_YES",
        "weight": "1000000000000000000"
      }
    ]
  }
}

Using v1:

cosmos.gov.v1.Query/Vote

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1","voter":"cosmos1.."}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Vote

Example Output:

{
  "vote": {
    "proposalId": "1",
    "voter": "cosmos1..",
    "option": "VOTE_OPTION_YES",
    "options": [
      {
        "option": "VOTE_OPTION_YES",
        "weight": "1.000000000000000000"
      }
    ]
  }
}

Votes

The Votes endpoint allows users to query all votes for a given proposal.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Votes

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Votes

Example Output:

{
  "votes": [
    {
      "proposalId": "1",
      "voter": "cosmos1..",
      "options": [
        {
          "option": "VOTE_OPTION_YES",
          "weight": "1000000000000000000"
        }
      ]
    }
  ],
  "pagination": {
    "total": "1"
  }
}

Using v1:

cosmos.gov.v1.Query/Votes

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Votes

Example Output:

{
  "votes": [
    {
      "proposalId": "1",
      "voter": "cosmos1..",
      "options": [
        {
          "option": "VOTE_OPTION_YES",
          "weight": "1.000000000000000000"
        }
      ]
    }
  ],
  "pagination": {
    "total": "1"
  }
}

Params

The Params endpoint allows users to query all parameters for the gov module.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Params

Example:

grpcurl -plaintext \
    -d '{"params_type":"voting"}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Params

Example Output:

{
  "votingParams": {
    "votingPeriod": "172800s"
  },
  "depositParams": {
    "maxDepositPeriod": "0s"
  },
  "tallyParams": {
    "quorum": "MA==",
    "threshold": "MA==",
    "vetoThreshold": "MA=="
  }
}

Using v1:

cosmos.gov.v1.Query/Params

Example:

grpcurl -plaintext \
    -d '{"params_type":"voting"}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Params

Example Output:

{
  "votingParams": {
    "votingPeriod": "172800s"
  }
}

Deposit

The Deposit endpoint allows users to query a deposit for a given proposal from a given depositor.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Deposit

Example:

grpcurl -plaintext \
    '{"proposal_id":"1","depositor":"cosmos1.."}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Deposit

Example Output:

{
  "deposit": {
    "proposalId": "1",
    "depositor": "cosmos1..",
    "amount": [
      {
        "denom": "stake",
        "amount": "10000000"
      }
    ]
  }
}

Using v1:

cosmos.gov.v1.Query/Deposit

Example:

grpcurl -plaintext \
    '{"proposal_id":"1","depositor":"cosmos1.."}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Deposit

Example Output:

{
  "deposit": {
    "proposalId": "1",
    "depositor": "cosmos1..",
    "amount": [
      {
        "denom": "stake",
        "amount": "10000000"
      }
    ]
  }
}

deposits

The Deposits endpoint allows users to query all deposits for a given proposal.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/Deposits

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/Deposits

Example Output:

{
  "deposits": [
    {
      "proposalId": "1",
      "depositor": "cosmos1..",
      "amount": [
        {
          "denom": "stake",
          "amount": "10000000"
        }
      ]
    }
  ],
  "pagination": {
    "total": "1"
  }
}

Using v1:

cosmos.gov.v1.Query/Deposits

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1.Query/Deposits

Example Output:

{
  "deposits": [
    {
      "proposalId": "1",
      "depositor": "cosmos1..",
      "amount": [
        {
          "denom": "stake",
          "amount": "10000000"
        }
      ]
    }
  ],
  "pagination": {
    "total": "1"
  }
}

TallyResult

The TallyResult endpoint allows users to query the tally of a given proposal.

Using legacy v1beta1:

cosmos.gov.v1beta1.Query/TallyResult

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1beta1.Query/TallyResult

Example Output:

{
  "tally": {
    "yes": "1000000",
    "abstain": "0",
    "no": "0",
    "noWithVeto": "0"
  }
}

Using v1:

cosmos.gov.v1.Query/TallyResult

Example:

grpcurl -plaintext \
    -d '{"proposal_id":"1"}' \
    localhost:9090 \
    cosmos.gov.v1.Query/TallyResult

Example Output:

{
  "tally": {
    "yes": "1000000",
    "abstain": "0",
    "no": "0",
    "noWithVeto": "0"
  }
}

REST

A user can query the gov module using REST endpoints.

proposal

The proposals endpoint allows users to query a given proposal.

Using legacy v1beta1:

/cosmos/gov/v1beta1/proposals/{proposal_id}

Example:

curl localhost:1317/cosmos/gov/v1beta1/proposals/1

Example Output:

{
  "proposal": {
    "proposal_id": "1",
    "content": null,
    "status": "PROPOSAL_STATUS_VOTING_PERIOD",
    "final_tally_result": {
      "yes": "0",
      "abstain": "0",
      "no": "0",
      "no_with_veto": "0"
    },
    "submit_time": "2022-03-28T11:50:20.819676256Z",