A proposal lifecycle defines the formal process from a governance idea's inception to its on-chain execution. It is a critical component of a DAO's smart contract architecture, establishing the rules for proposal submission, voting, timelocks, and execution. Unlike simple token-weighted voting, a full lifecycle manages state transitions (e.g., Pending, Active, Succeeded, Queued, Executed) and enforces security checks. Protocols like Compound Governor Bravo and OpenZeppelin's Governor provide standardized, audited contracts that implement these lifecycles, which you can fork and customize.
How to Design a Proposal Lifecycle for Your Protocol
How to Design a Proposal Lifecycle for Your Protocol
A well-structured proposal lifecycle is the operational backbone of decentralized governance, ensuring decisions are made transparently, securely, and efficiently.
The first design step is defining the proposal threshold and voting parameters. The threshold determines the minimum voting power (often in governance tokens like UNI or AAVE) required to submit a proposal, preventing spam. Key voting parameters include the voting delay (time between proposal submission and voting start), voting period (duration of the vote, typically 3-7 days), and the quorum (minimum participation required for validity). For example, Uniswap governance uses a 2.5 million UNI proposal threshold and a 7-day voting period.
Security is enforced through timelocks and execution logic. A timelock contract, like OpenZeppelin's TimelockController, sits between the governor and the protocol's core contracts. After a vote succeeds, the proposal action is queued in the timelock for a mandatory delay (e.g., 48 hours). This grace period allows users to review the executed code and exit positions if necessary, serving as a final safeguard against malicious proposals. The execution call is then made from the timelock, not directly from the governor.
Your lifecycle must also handle proposal cancellation and vote delegation. Cancellation mechanisms allow a DAO's guardian (or a security council) to halt malicious proposals, though this introduces centralization trade-offs. Delegation, as seen in Compound, lets token holders delegate their voting power to other addresses, enabling participation without constant wallet interaction. These features are implemented in the governor's state machine, ensuring a proposal can only be canceled in specific states before execution.
Finally, integrate with off-chain signaling and Tally or Snapshot. For complex or non-critical decisions, many DAOs use Snapshot for gas-free, off-chain sentiment polling. The on-chain lifecycle is then reserved for binding treasury or parameter changes. Your design should specify which proposal types trigger which process. Test your complete lifecycle thoroughly on a testnet using frameworks like Hardhat or Foundry, simulating both normal and edge-case execution paths before mainnet deployment.
Prerequisites for Designing a Governance Lifecycle
Before you write a single line of governance code, you must define the core parameters that will shape your protocol's decision-making process.
A robust governance lifecycle begins with a clear proposal framework. This defines the rules for submission, voting, and execution. You must decide on the proposal types your system will support, such as parameter changes, treasury spending, or smart contract upgrades. Each type may require different thresholds and processes. For example, a simple parameter tweak might need a 51% majority, while a critical upgrade could require an 80% supermajority and a multi-day timelock. The framework also specifies who can submit proposals: is it any token holder, a delegated council, or a multi-signature wallet?
Next, establish the voting mechanism and tokenomics. The choice between token-weighted voting (like Compound's COMP), quadratic voting (like Gitcoin Grants), or conviction voting (like 1Hive's Honey) has profound implications for decentralization and attack resistance. Your governance token's distribution is equally critical. If tokens are overly concentrated, governance becomes centralized. Protocols often implement vesting schedules, delegation features, and mechanisms to discourage voter apathy. The votingPeriod and votingDelay are concrete Solidity variables you'll need to set, balancing efficiency with sufficient deliberation time.
You must also design the security and execution layer. This includes fail-safes like a timelock contract, which delays proposal execution after a vote passes, giving users a final window to exit if they disagree with the outcome. Consider integrating a guardian or pause role for emergency responses, though this introduces centralization trade-offs. The execution logic itself must be meticulously audited; a proposal that calls executeProposal(address target, bytes calldata data) can perform any on-chain action, making it a prime attack vector. Testing with forked mainnet simulations using tools like Tenderly or Foundry is non-negotiable.
Finally, define the lifecycle stages and user experience. A typical lifecycle flows from Draft → Temperature Check → Formal Proposal → Voting → Queued (Timelock) → Executed. Each stage needs clear UI representation and on-chain signaling. For developers, this means writing event-emitting functions like ProposalCreated(uint256 proposalId) and state-checking view functions. Off-chain components, such as a Snapshot page for signaling or a forum for discussion, are often prerequisites for a healthy process. The design must account for proposal updating, cancellation, and handling of failed executions.
Core Stages of a Governance Proposal
A structured proposal lifecycle is critical for secure, efficient, and legitimate on-chain governance. This guide outlines the essential stages, from ideation to execution, used by protocols like Uniswap, Compound, and Aave.
The first stage is Ideation and Discussion. Proposals should be socialized off-chain in community forums like the Uniswap Governance Forum or Compound's Discord. This phase gathers feedback, builds consensus, and refines the proposal's technical and economic details before any on-chain action. A well-discussed proposal with clear specifications and a Snapshot temperature check significantly increases its chances of success and reduces governance fatigue.
Next is the Formal Submission stage. A proposer deposits governance tokens (e.g., UNI, COMP) to create an on-chain proposal contract. This contract encodes the executable logic, such as a calldata payload for a Timelock controller. In Compound's Governor Bravo, for example, this involves calling propose() with targets, values, and calldata arrays. The deposit acts as a spam-prevention mechanism and is typically refunded if the proposal passes a preliminary vote threshold.
Following submission, the proposal enters a Voting Delay period—a mandatory waiting time (e.g., 1 day) before voting begins. This allows token holders time to review the final proposal code. The Voting Period then opens, during which delegated token holders cast votes for For, Against, or Abstain. Voting power is typically snapshotted at the proposal creation block to prevent manipulation. Successful proposals must meet a quorum (minimum voting power participation) and a majority threshold.
After a successful vote, a Timelock Delay is a critical security stage. The proposal actions are queued in a Timelock contract (like OpenZeppelin's) for a set period (e.g., 2 days for Uniswap). This delay gives users a final window to exit systems or react if they disagree with the passed proposal, serving as a last-resort safeguard against malicious governance takeover.
The final stage is Execution. Once the timelock delay expires, any address (usually the proposer or a keeper) can call the execute function on the proposal contract. This triggers the encoded transactions, such as upgrading a protocol contract or adjusting a treasury parameter. Failed proposals that do not meet quorum or are defeated are simply closed, with the proposer's deposit typically forfeited.
Designing this lifecycle involves key parameters: votingDelay, votingPeriod, proposalThreshold, quorum, and timelockDelay. Protocols balance these for security versus agility. For instance, a longer timelock enhances safety but slows iteration. The lifecycle ensures governance is deliberate, transparent, and resistant to attacks, transforming token holder sentiment into executable protocol upgrades.
Proposal Stage Comparison: Compound vs. Uniswap
A side-by-side breakdown of the formal proposal stages for two leading on-chain governance protocols.
| Stage / Parameter | Compound Governance | Uniswap Governance |
|---|---|---|
Proposal Submission Threshold | 65,000 COMP | 10,000,000 UNI |
Proposal Voting Period Duration | 3 days | 7 days |
Proposal Timelock Delay | 2 days | 2 days |
Quorum Requirement | 400,000 COMP | 40,000,000 UNI |
Approval Threshold (For) |
|
|
Proposal Upgrade Execution | Queued in Timelock, then executed | Queued in Timelock, then executed |
Delegated Voting | ||
Emergency Proposal Mechanism |
How to Design a Proposal Lifecycle for Your Protocol
A well-defined proposal lifecycle is a critical governance primitive for decentralized protocols. This guide explains how to design and implement its state transitions and smart contract logic.
The proposal lifecycle defines the formal process for submitting, reviewing, voting on, and executing changes to a protocol. It is the core state machine of on-chain governance. A robust design must manage several key states: Pending, Active, Succeeded, Queued, Executed, and Canceled or Defeated. Each transition between these states is triggered by specific conditions, such as reaching a quorum of votes or passing a timelock delay. The smart contract logic must enforce these rules immutably to prevent manipulation.
Start by defining the state variables and modifiers. A typical Solidity implementation uses an enum for the proposal state and a struct to bundle proposal data like proposer, targets, values, calldatas, startBlock, endBlock, and voteCounts. Crucial access control is enforced with modifiers like onlyGovernance or onlyProposer. The transition from Pending to Active is usually time-based, triggered when the current block number exceeds the startBlock. This initial design ensures proposals cannot be voted on prematurely.
The voting phase logic is the most complex component. When a proposal is Active, token holders cast votes, which are tallied in the proposal struct. The contract must check that votes are cast within the voting period (block.number <= endBlock) and adhere to the chosen voting model (e.g., single-choice, quadratic). The transition to Succeeded or Defeated occurs after endBlock by calling a queue or finalize function, which evaluates if the proposal met minimum quorum and support thresholds. Failed proposals should be marked as Defeated to prevent further action.
For security, successful proposals should enter a Queued state before execution. This introduces a mandatory timelock delay, a critical defense that allows users to review the finalized proposal's bytecode and exit the system if they disagree. The queue function typically sets an eta (estimated time of arrival) by adding the timelock duration to the current block timestamp. Only after block.timestamp >= eta can the execute function be called, transitioning the proposal to Executed and performing the low-level calls to the target contracts specified in the proposal.
Consider edge cases and failure modes. A proposal may be Canceled by a guardian or the proposer under predefined conditions, such as a discovered vulnerability. The logic must ensure canceled or defeated proposals cannot be queued or executed. Gas optimization is also key; storing vote counts in a struct and using bitpacking for status flags can reduce storage costs. Always implement events like ProposalCreated, VoteCast, ProposalQueued, and ProposalExecuted for off-chain indexing and transparency.
Reference established implementations like OpenZeppelin's Governor contracts or Compound's GovernorBravo for audited patterns. Test your state machine thoroughly with tools like Foundry, simulating all possible transitions and malicious scenarios. A well-designed lifecycle is not just a feature; it's the foundation of your protocol's legitimacy and resilience, ensuring upgrades are transparent, secure, and reflective of community will.
Key Smart Contract Components
A robust governance system requires a well-defined proposal lifecycle. These are the core smart contract components you need to implement.
Proposal Factory
The contract that creates new proposal instances. It handles initial parameter validation and deploys a new, isolated contract for each proposal. Key functions include:
- Setting the proposal creator and initial timestamps.
- Defining the proposal type (e.g., parameter change, treasury spend).
- Storing a reference to the proposal's execution logic.
This pattern prevents state pollution in the main governance contract.
Voting Power & Snapshot
Determines voter eligibility and weight. This module must provide a cryptographically verifiable snapshot of token holdings at a specific block. Implementation options:
- ERC-20 Snapshot: Uses a merkle tree of balances from a past block.
- ERC-5805 (Delegation): Integrates with token delegation standards like OpenZeppelin's Votes.
- Time-weighted voting: Calculates power based on token age (ve-token model).
Without a secure snapshot, voters can manipulate outcomes by transferring tokens.
Voting & Tallying Logic
The core contract that manages the voting period and calculates results. It must support multiple voting strategies:
- Single-choice voting: For/Against/Abstain.
- Quadratic voting: Cost scales with the square of votes cast.
- Ranked-choice voting: More complex tallying for multiple options.
This contract enforces voting deadlines, prevents double-voting, and emits events for off-chain indexing. The final tally determines if the proposal passes the predefined quorum and support threshold.
Proposal State Machine
Manages the lifecycle status of each proposal. A typical flow is:
- Pending: Created, awaiting the start of the voting delay.
- Active: Voting is open.
- Succeeded/Defeated: Voting ended; met or failed thresholds.
- Queued: Approved proposal sent to the Timelock.
- Executed/Canceled: Final state.
The state is enforced on-chain, preventing proposals from moving backward or skipping steps. Failed proposals can often be resubmitted.
Execution Payload
The encoded function calls that will run if the proposal passes. This is often stored as an array of target, value, and calldata. Design considerations:
- Multisend: Bundle multiple operations into a single proposal.
- Delegatecall: Allows upgrading proxy contracts (high risk).
- Validation: Simulate the call during voting to ensure it won't revert.
Poorly constructed payloads are a common source of failed executions, even for passed proposals.
Setting Voting Parameters: Quorum, Thresholds, and Duration
A protocol's governance is defined by its proposal lifecycle. This guide explains how to configure the core parameters that determine how proposals pass or fail.
The proposal lifecycle is the sequence of states a governance proposal moves through, from submission to execution. Its design directly impacts a DAO's security, efficiency, and responsiveness. The three most critical parameters to define are the quorum, approval threshold, and voting duration. These settings create a balance between preventing malicious proposals and enabling legitimate protocol upgrades. Poorly configured parameters can lead to voter apathy, governance attacks, or complete paralysis.
Quorum is the minimum percentage of the total voting power that must participate for a vote to be valid. It ensures decisions reflect a meaningful portion of the community. A quorum set too high (e.g., 80%) can make passing any proposal nearly impossible, leading to stagnation. A quorum set too low (e.g., 1%) allows a small, potentially malicious group to control outcomes. Many protocols, like Compound, use a dynamic quorum that adjusts based on recent proposal activity to find a healthy equilibrium.
Approval Threshold (or passing threshold) is the percentage of participating votes (not total supply) required for a "Yes" outcome. This is separate from quorum. For example, a proposal might require a 4% quorum and a 51% approval threshold. High-stakes proposals, such as upgrading a core contract, often use a supermajority threshold (e.g., 67% or 75%) to require broader consensus. The threshold is typically applied within the execute function of the governance contract, which reverts if the vote does not meet the required margin.
Voting Duration is the length of time the vote remains open. A standard duration is 3-7 days, providing sufficient time for deliberation across timezones while maintaining momentum. Longer durations (e.g., 2 weeks) may be used for monumental changes but increase the window for market conditions to shift. The duration is enforced by checking block.timestamp against the proposal's start time. It's crucial to also design a timelock delay between a proposal passing and its execution, allowing users to exit if they disagree with the outcome.
Here is a simplified Solidity example illustrating how these parameters are validated during proposal execution:
solidityfunction executeProposal(uint proposalId) external { Proposal storage p = proposals[proposalId]; require(block.timestamp >= p.startTime + VOTING_DURATION, "Voting active"); require(p.forVotes + p.againstVotes >= getQuorum(), "Quorum not met"); require(p.forVotes * 10000 / (p.forVotes + p.againstVotes) >= APPROVAL_THRESHOLD_BPS, "Threshold not met"); // Execute the proposal's actions }
In this code, APPROVAL_THRESHOLD_BPS is in basis points (e.g., 5100 for 51%).
When designing your lifecycle, start with conservative parameters and iterate based on community feedback. Monitor metrics like proposal turnout and the margin of victory. Tools like Tally and Boardroom provide analytics for this purpose. Remember, the goal is to create a system that is both secure against capture and agile enough to adapt. Your parameter choices will define your protocol's political character for years to come.
Implementation Code Examples
Governance Contract Skeleton
This example shows the foundational structure for a proposal lifecycle using OpenZeppelin's Governor contracts, the most widely adopted standard.
solidity// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Governor, GovernorSettings} from "@openzeppelin/contracts/governance/Governor.sol"; import {GovernorCountingSimple} from "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol"; contract ProtocolGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes { constructor(IVotes _token) Governor("Protocol Governor") GovernorSettings(1 /* 1 block */, 50400 /* 1 week */, 0) GovernorVotes(_token) {} function votingDelay() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingDelay(); } function votingPeriod() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingPeriod(); } function quorum(uint256 blockNumber) public pure override returns (uint256) { return 1000e18; // 1000 token quorum } function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { return 100e18; // 100 token threshold to propose } }
Key Components:
votingDelay: Blocks before voting starts on a proposal.votingPeriod: Duration of the voting phase.quorum: Minimum voting power required for a proposal to be valid.proposalThreshold: Minimum token balance required to submit a proposal.
Governance Lifecycle FAQ
Common questions and solutions for designing and implementing a robust, secure proposal lifecycle for on-chain governance protocols.
A standard on-chain governance lifecycle consists of four to five distinct stages, each with specific functions and security checks.
1. Proposal Submission: A user deposits a bond (e.g., in ETH or governance tokens) and submits the proposal's executable code or description to the governance contract. This stage often includes a timelock for review.
2. Voting Period: Token holders cast votes, typically weighted by their stake. Major implementations include:
- Compound's Governor Bravo: Snapshot-based voting with a quorum requirement.
- OpenZeppelin's Governor: Modular contracts supporting various voting strategies.
3. Timelock Execution Delay: After passing, the proposal enters a mandatory waiting period (e.g., 2 days for Uniswap). This is a critical security feature, allowing users to exit systems if a malicious proposal passes.
4. Execution: The approved proposal's calldata is executed, often via a TimelockController contract that acts as the protocol's owner.
5. (Optional) Grace/Challenge Period: Some systems like Optimism's Governance include a period for veto or challenge before final execution.
Resources and Further Reading
These resources cover real-world proposal lifecycle designs used by live protocols. Each card focuses on a specific layer, from off-chain signaling to on-chain execution, with concrete implementation details.
Conclusion and Next Steps
This guide has outlined the core components of a secure and effective governance lifecycle. The next step is to implement these concepts within your protocol.
A well-designed proposal lifecycle is a critical on-chain governance primitive. It transforms abstract community sentiment into executable protocol upgrades. The key components are a proposal factory for standardized creation, a timelock controller for security, and a voting strategy that aligns with your tokenomics. For example, Compound's Governor Bravo contract suite provides a battle-tested implementation of this pattern, using a Timelock contract to queue and execute successful proposals after a mandatory delay.
To begin implementation, choose a framework that matches your needs. For Ethereum and EVM chains, consider OpenZeppelin Governor for a modular base or Compound Governor Bravo for a more opinionated system. For Solana, the Realms platform offers a complete DAO toolkit. Your first technical steps should be: 1) deploying and configuring the timelock contract, 2) writing your governor contract that references it, and 3) integrating your chosen voting token or NFT. Thoroughly test all state transitions—from proposal creation through to execution—on a testnet.
After deployment, governance is an ongoing process. Use off-chain voting platforms like Snapshot for gas-free sentiment signaling before binding on-chain votes. Establish clear community guidelines in your forum to standardize proposal formatting and discussion requirements. Monitor key metrics such as proposal turnout, voting power concentration, and execution success rate. Continuously iterate based on community feedback; parameters like the voting period, quorum, and timelock duration often need adjustment as the protocol matures.
For further learning, review the source code and documentation of leading implementations. Study OpenZeppelin's Governor guide for modular design patterns and Compound's Governance documentation for real-world deployment insights. The goal is to create a system that is not only secure and functional but also accessible and engaging for your protocol's stakeholders, ensuring its long-term resilience and adaptability.