Skip to main content

Governance

ℹ️info

Terra's Governance module inherits from Cosmos SDK's gov module. This document is a stub, and mainly covers important Terra-specific notes on how it is used.

The Terra protocol is a decentralized public blockchain governed by community members. Governance is the democratic process that allows users and validators to make changes to the Terra protocol. Community members submit, vote, and implement proposals.

The governance module maintains the logic for all governance activity.

To learn how to vote with your staked Luna or submit proposals, visit the Terra Station governance guide.

If you are an advanced user and want to learn about submitting or voting on proposals using Terrad, visit the Governance section of the Terrad Reference.

Concepts

The following concepts describe the governance proposal procedure.

Drafting and submission

Proposals start as ideas within the community on Terra's Agora forum. After gaining support and feedback from the community, a proposer drafts and submits a proposal alongside an initial deposit.

To learn more about the different types of proposals, see the Proposal types section below.

Deposit period

After a proposal is submitted, it enters the deposit period, where it must reach a total minimum deposit of 512 Luna within 7 days from the time of its submission. The deposit threshold is reached when the sum of the initial deposit (from the proposer) and the deposits from all other interested network participants meet or exceed 512 Luna.

Deposits protect against unnecessary proposals and spam.

Deposits get refunded if all of the following conditions are met:

  • The minimum deposit of 512 Luna is reached within the 7-day deposit period.
  • Quorum is met: all casted votes represent more than 10% of all staked Luna.
  • The total number of NoWithVeto votes is less than 33.4% of the total vote.
  • A vote returns a majority of Yes or No votes.

Deposits are burned under any of the following conditions:

  • The minimum deposit of 512 Luna is not reached within the one-week deposit period.
  • Quorum is not met: the number of total votes after the one-week voting period is less than 10% of all staked Luna.
  • The number of NoWithVeto votes is above 33.4% of the total vote.

Voting period

If the minimum deposit has been reached before the end of the deposit period, then the proposal goes into a one-week voting period. While the proposal is in voting, holders of staked Luna can cast votes for the proposal.

Voting options

The 4 voting options available are:

  • Yes: in favor.
  • No: not in favor.
  • NoWithVeto: not in favor, and the creator should lose their deposit.
  • Abstain: neither for or against. An abstain vote counts toward meeting quorum but does not count toward the threshold.

Delegators vote using their staked Luna. One staked Luna equals one vote.

If a delegator does not specify a vote with their staked Luna, their vote defaults to the vote cast by the validator their Luna is staked to. Delegators can override a validator's vote at any time during the voting period by voting with their staked Luna.

Tallying

The logic for tallying votes can be found in the tally.go file of the Cosmos SDK.

For a proposal to pass, the following conditions must be met.

  1. Voter participation must be at least quorum (QQ). The current quorum value is 10% of all staked Luna.
Yes+No+NoWithVeto+AbstainTotalStakedTokensQ\frac{Yes + No + NoWithVeto + Abstain}{Total Staked Tokens} \ge Q
  1. The ratio of NoWithVeto votes must be less than veto (VV). The current veto value is 33%.
NoWithVetoYes+No+NoWithVeto+AbstainV\frac{NoWithVeto}{Yes + No + NoWithVeto + Abstain} \le V
  1. The ratio of Yes votes must be greater than threshold (TT). The current threshold value is 50%. Abstain votes are not included in the threshold tally.
YesYes+No+NoWithVeto>T\frac{Yes}{Yes + No + NoWithVeto} \gt T

If a proposal meets all of the previous conditions, it passes. If a proposal fails to meet any of the previous conditions, it fails. Proposals that get rejected with veto do not get their deposits refunded. The parameters quorum, veto, and threshold exist as blockchain parameters in the Governance module.

☢️caution

Deposits will not be refunded for proposals that are rejected with veto, do not meet quorum, or fail to reach the minimum deposit during the deposit period. Non-refunded deposits are burned.

Proposal Implementation

Once a governance proposal passes, the changes described are put into effect by the proposal handler. Generic proposals such as a TextProposal must be reviewed by Terra protocol developers and the community for decisions on how to manually implement them.

Although parameter changes get updated immediately, they generally are not put into effect until the next epoch operation. Epochs occur every 100800 blocks or roughly every 7.7 days, given a 6.6-second block time.

Data

Proposal


_14
type Proposal struct {
_14
Content `json:"content" yaml:"content"` // Proposal content interface
_14
_14
ProposalID uint64 `json:"id" yaml:"id"` // ID of the proposal
_14
Status ProposalStatus `json:"proposal_status" yaml:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected}
_14
FinalTallyResult TallyResult `json:"final_tally_result" yaml:"final_tally_result"` // Result of Tallies
_14
_14
SubmitTime time.Time `json:"submit_time" yaml:"submit_time"` // Time of the block where TxGovSubmitProposal was included
_14
DepositEndTime time.Time `json:"deposit_end_time" yaml:"deposit_end_time"` // Time that the Proposal would expire if deposit amount isn't met
_14
TotalDeposit sdk.Coins `json:"total_deposit" yaml:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit
_14
_14
VotingStartTime time.Time `json:"voting_start_time" yaml:"voting_start_time"` // Time of the block where MinDeposit was reached. -1 if MinDeposit is not reached
_14
VotingEndTime time.Time `json:"voting_end_time" yaml:"voting_end_time"` // Time that the VotingPeriod for this proposal will end and votes will be tallied
_14
}

A Proposal is a data structure representing a petition for a change that is submitted to the blockchain alongside a deposit. Once its deposit reaches a certain MinDeposit, the proposal is confirmed and voting opens. Bonded Luna holders can then send TxGovVote transactions to vote on the proposal. Terra currently follows a simple voting scheme of 1 Bonded Luna = 1 Vote.

The Content of a proposal is the interface that contains the information about the Proposal, such as the title, description, and any notable changes. A Content type can be implemented by any module. The ProposalRoute of the Content returns a string that must be used to route the handler of the Content in the Governance keeper. This process allows the governance keeper to execute proposal logic implemented by any module. If a proposal passes, the handler is executed. Only if the handler is successful does the state get persisted and the proposal finally pass. Otherwise, the proposal is rejected.

Message Types

MsgSubmitProposal


_5
type MsgSubmitProposal struct {
_5
Content Content `json:"content" yaml:"content"`
_5
InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive
_5
Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` // Address of the proposer
_5
}

MsgDeposit


_5
type MsgDeposit struct {
_5
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal
_5
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor
_5
Amount sdk.Coins `json:"amount" yaml:"amount"` // Coins to add to the proposal's deposit
_5
}

MsgVote


_5
type MsgVote struct {
_5
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal
_5
Voter sdk.AccAddress `json:"voter" yaml:"voter"` // Address of the voter
_5
Option VoteOption `json:"option" yaml:"option"` // Option from OptionSet chosen by the voter
_5
}

Proposal types

Text Proposal


_4
type TextProposal struct {
_4
Title string `json:"title" yaml:"title"`
_4
Description string `json:"description" yaml:"description"`
_4
}

Text Proposals are used to create general-purpose petitions, such as asking core developers or community members to implement a specific feature. The community can reference a passed Text Proposal to the core developers or community members to indicate that a feature that potentially requires a soft or hard fork is in significant demand.

Parameter Change Proposals


_12
type ParameterChangeProposal struct {
_12
Title string `json:"title" yaml:"title"`
_12
Description string `json:"description" yaml:"description"`
_12
Changes []ParamChange `json:"changes" yaml:"changes"`
_12
}
_12
_12
type ParamChange struct {
_12
Subspace string `json:"subspace" yaml:"subspace"`
_12
Key string `json:"key" yaml:"key"`
_12
Subkey string `json:"subkey,omitempty" yaml:"subkey,omitempty"`
_12
Value string `json:"value" yaml:"value"`
_12
}

Parameter Change Proposals are a special type of proposal which, once passed, will automatically go into effect by directly altering the network's specified parameter.

Software Upgrade Proposals

This type of proposal requires validators to update their node software to a new version at a specified block height.

🔥danger

Software upgrade proposals can be difficult to execute. Exercise caution when using this proposal type, as you may lose your deposit due to an incorrect proposal.

Transitions

End-Block

This section was taken from the official Cosmos SDK docs and placed here for your convenience to understand the Governance process.

ProposalProcessingQueue is a queue (queue[proposalID]) containing all the ProposalIDs of proposals that reach MinDeposit. At the end of each block, 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.


_52
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
_52
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
_52
_52
validators = Keeper.getAllValidators()
_52
tmpValMap := map(sdk.AccAddress)ValidatorGovInfo
_52
_52
// Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
_52
for each validator in validators
_52
tmpValMap(validator.OperatorAddr).Minus = 0
_52
_52
// Tally
_52
voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
_52
for each (voterAddress, vote) in voterIterator
_52
delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter
_52
_52
for each delegation in delegations
_52
// make sure delegation.Shares does NOT include shares being unbonded
_52
tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
_52
proposal.updateTally(vote, delegation.Shares)
_52
_52
_, isVal = stakingKeeper.getValidator(voterAddress)
_52
if (isVal)
_52
tmpValMap(voterAddress).Vote = vote
_52
_52
tallyingParam = load(GlobalParams, 'TallyingParam')
_52
_52
// Update tally if validator voted they voted
_52
for each validator in validators
_52
if tmpValMap(validator).HasVoted
_52
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
_52
_52
// Check if proposal is accepted or rejected
_52
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
_52
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto)
_52
// proposal was accepted at the end of the voting period
_52
// refund deposits (non-voters already punished)
_52
for each (amount, depositor) in proposal.Deposits
_52
depositor.AtomBalance += amount
_52
_52
stateWriter, err := proposal.Handler()
_52
if err != nil
_52
// proposal passed but failed during state execution
_52
proposal.CurrentStatus = ProposalStatusFailed
_52
else
_52
// proposal pass and state is persisted
_52
proposal.CurrentStatus = ProposalStatusAccepted
_52
stateWriter.save()
_52
else
_52
// proposal was rejected
_52
proposal.CurrentStatus = ProposalStatusRejected
_52
_52
store(Governance, <proposalID|'proposal'>, proposal)

Parameters

The subspace for the Governance module is gov.


_14
type DepositParams struct {
_14
MinDeposit sdk.Coins `json:"min_deposit,omitempty" yaml:"min_deposit,omitempty"`
_14
MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty" yaml:"max_deposit_period,omitempty"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
_14
}
_14
_14
type TallyParams struct {
_14
Quorum sdk.Dec `json:"quorum,omitempty" yaml:"quorum,omitempty"`
_14
Threshold sdk.Dec `json:"threshold,omitempty" yaml:"threshold,omitempty"`
_14
Veto sdk.Dec `json:"veto,omitempty" yaml:"veto,omitempty"`
_14
}
_14
_14
type VotingParams struct {
_14
VotingPeriod time.Duration `json:"voting_period,omitempty" yaml:"voting_period,omitempty"`
_14
}

Genesis parameters

The genesis parameters for the governance module outlined in the Genesis Builder Script are as follows:


_20
# Gov: change min deposit to 512 LUNA and deposit period to 7 days
_20
genesis['app_state']['gov']['deposit_params'] = {
_20
'max_deposit_period': '604800s', # 7days
_20
'min_deposit': [{
_20
'denom': DENOM_LUNA,
_20
'amount': '512000000'
_20
}],
_20
}
_20
_20
# Gov: set tally params quorum to 10%
_20
genesis['app_state']['gov']['tally_params'] = {
_20
'quorum': '0.100000000000000000',
_20
'threshold': '0.500000000000000000',
_20
'veto_threshold': '0.334000000000000000'
_20
}
_20
_20
# Gov: set voting period to 7 days
_20
genesis['app_state']['gov']['voting_params'] = {
_20
'voting_period': '604800s'
_20
}

MinDeposit

  • type: Coins
  • denom: uluna
  • amount: 512000000

The minimum deposit amount for a proposal to enter a voting period. Currently 512 Luna.

MaxDepositPeriod

  • type: time.Duration (seconds)
  • "max_deposit_period": "604800s"

The maximum period of time for a proposal to meet the minimum deposit. Currently 1 week.

Quorum

  • type: Dec
  • "quorum": "0.100000000000000000",

The minimum number of votes needed for a proposal to be valid. Currently, 10% of all staked Luna must vote to reach Quorum.

Threshold

  • type: Dec
  • default value: 50%

The minimum number of Yes votes needed for a proposal to pass. Currently, 50% of all votes must be Yes. Abstain votes are excluded from this tally.

Veto

  • type: Dec
  • default value: 0.334

The minimum value of NoWithVeto votes needed for a proposal to be vetoed. If 33.4% or more of the total votes cast are NoWithVeto, the proposal fails, and the creator's deposit is not returned.

VotingPeriod

  • type: time.Duration (seconds)
  • "voting_period": "604800s"

Length of the voting period. Currently 1 week.