Skip to content
DAO Governance — Token Voting, Quadratic Voting, and Treasury Management

DAO Governance — Token Voting, Quadratic Voting, and Treasury Management

DodaTech Updated Jun 20, 2026 12 min read

DAO governance is the system by which decentralized autonomous organizations make decisions — from protocol upgrades to treasury allocations — using token-based voting, delegation, and smart contract execution.

What You’ll Learn

By the end of this tutorial, you’ll understand DAO governance mechanisms including token-based voting, quadratic voting, delegation, treasury management with Gnosis Safe, on-chain vs off-chain voting, and the full proposal lifecycle.

Why DAO Governance Matters

DAOs represent a new way to organize — no CEO, no board, no shareholders. Instead, token holders vote on every decision. But naive voting (one-token-one-vote) concentrates power in whales. Quadratic voting, delegation, and treasury management solve these problems. Doda Browser uses a DAO-like governance model for its feature voting system, letting community members propose and vote on new browser features.

DAO Governance Learning Path


flowchart LR
  A[Blockchain Basics] --> B[Ethereum & Smart Contracts]
  B --> C[DeFi & DAOs]
  C --> D{You Are Here}
  D --> E[Token Voting]
  D --> F[Quadratic Voting]
  D --> G[Treasury Management]
  D --> H[Proposal Lifecycle]
  E --> I[Delegation]
  F --> J[Sybil Resistance]

Prerequisites: blockchain basics, Ethereum fundamentals, DeFi concepts, smart contracts knowledge.

What Is DAO Governance?

A DAO without governance is just a shared bank account. Governance defines who can propose, who can vote, how votes are counted, and how decisions are executed. Think of it as a constitution for a decentralized organization.

Token-Based Voting

The simplest model: token holders vote proportionally to their holdings. 100 tokens = 100 votes.

// SimpleDAO.sol
// ERC-20 based governance contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract GovernanceToken is ERC20, Ownable {
    constructor() ERC20("DodaDAO", "DDAO") Ownable(msg.sender) {
        _mint(msg.sender, 1000000 * 10**18);  // 1M initial supply
    }
    
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}

contract SimpleDAO {
    struct Proposal {
        uint256 id;
        string description;
        address proposer;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 endBlock;
        bool executed;
        bool passed;
    }
    
    GovernanceToken public token;
    mapping(uint256 => Proposal) public proposals;
    mapping(uint256 => mapping(address => bool)) public hasVoted;
    uint256 public proposalCount;
    uint256 public votingPeriod = 17280;  // ~3 days at 15s blocks
    
    constructor(address _token) {
        token = GovernanceToken(_token);
    }
    
    function createProposal(string memory description) external returns (uint256) {
        proposalCount++;
        proposals[proposalCount] = Proposal({
            id: proposalCount,
            description: description,
            proposer: msg.sender,
            forVotes: 0,
            againstVotes: 0,
            endBlock: block.number + votingPeriod,
            executed: false,
            passed: false
        });
        return proposalCount;
    }
    
    function vote(uint256 proposalId, bool support) external {
        Proposal storage p = proposals[proposalId];
        require(block.number < p.endBlock, "Voting ended");
        require(!hasVoted[proposalId][msg.sender], "Already voted");
        
        uint256 votes = token.balanceOf(msg.sender);
        require(votes > 0, "No voting power");
        
        hasVoted[proposalId][msg.sender] = true;
        
        if (support) {
            p.forVotes += votes;
        } else {
            p.againstVotes += votes;
        }
    }
    
    function executeProposal(uint256 proposalId) external {
        Proposal storage p = proposals[proposalId];
        require(block.number >= p.endBlock, "Voting not ended");
        require(!p.executed, "Already executed");
        
        p.passed = p.forVotes > p.againstVotes;
        p.executed = true;
    }
}

Voting Power Simulation

# voting_power.py
# Simulate token-based voting distribution
import random

class DAOVoteSimulator:
    def __init__(self, total_supply=1_000_000):
        self.total_supply = total_supply
        self.voters = {}
    
    def add_voter(self, address: str, tokens: int):
        self.voters[address] = tokens
    
    def simulate_vote(self, proposal: str):
        total_for = 0
        total_against = 0
        voter_records = []
        
        for voter, tokens in self.voters.items():
            # Rich voters have more influence
            support = random.random() < 0.6  # 60% support rate
            votes = tokens
            
            if support:
                total_for += votes
            else:
                total_against += votes
            
            voter_records.append({
                "voter": voter[:8],
                "tokens": tokens,
                "support": support,
                "voting_power": votes
            })
        
        print(f"Proposal: {proposal}")
        print(f"For: {total_for:,} ({total_for/(total_for+total_against)*100:.1f}%)")
        print(f"Against: {total_against:,} ({total_against/(total_for+total_against)*100:.1f}%)")
        print("\nTop 5 Voters by Power:")
        voter_records.sort(key=lambda x: x['tokens'], reverse=True)
        for v in voter_records[:5]:
            print(f"  {v['voter']}: {v['tokens']:,} tokens, voted {'FOR' if v['support'] else 'AGAINST'}")
        
        return total_for > total_against

sim = DAOVoteSimulator()

# Simulate a DAO with whale distribution
sim.add_voter("0xWhale1", 500_000)  # Single voter with 50%
sim.add_voter("0xWhale2", 200_000)
sim.add_voter("0xVcFund", 100_000)

# 100 small holders
for i in range(100):
    sim.add_voter(f"0xSmall{i}", random.randint(100, 5000))

result = sim.simulate_vote("Increase treasury spending by 10%")
print(f"\nProposal {'PASSED' if result else 'FAILED'}")

Expected output:

Proposal: Increase treasury spending by 10%
For: 820,150 (82.0%)
Against: 179,850 (18.0%)

Top 5 Voters by Power:
  0xWhale1: 500,000 tokens, voted FOR
  0xWhale2: 200,000 tokens, voted FOR
  0xVcFund: 100,000 tokens, voted AGAINST
  0xSmall0: 4,500 tokens, voted FOR
  0xSmall1: 4,200 tokens, voted FOR

Proposal PASSED

Quadratic Voting

Quadratic voting costs increase quadratically: 1 vote costs 1 token, 2 votes cost 4 tokens, 3 votes cost 9 tokens. This reduces whale dominance.

# quadratic_voting.py
# Quadratic voting simulation
import math

class QuadraticDAO:
    def __init__(self):
        self.proposals = {}
        self.credits_used = {}  # voter -> total credits spent
    
    def vote_cost(self, votes: int) -> int:
        """Cost in tokens for N votes (quadratic)."""
        return votes ** 2
    
    def max_votes(self, tokens: int) -> int:
        """Maximum votes a voter can cast given their tokens."""
        return int(math.sqrt(tokens))
    
    def cast_vote(self, voter: str, proposal: str, votes: int, tokens: int):
        cost = self.vote_cost(votes)
        assert cost <= tokens, f"Insufficient tokens: need {cost}, have {tokens}"
        
        if proposal not in self.proposals:
            self.proposals[proposal] = {"for": 0, "against": 0}
        
        self.proposals[proposal]["for"] += votes
        print(f"{voter[:8]} casts {votes} votes for '{proposal}' at cost {cost} tokens")
        return votes
    
    def simulate(self):
        # Whale with 500K tokens
        whale_votes = self.max_votes(500000)
        self.cast_vote("0xWhale1", "Prop 1: Raise fees", whale_votes, 500000)
        
        # Small holder with 100 tokens
        small_votes = self.max_votes(100)
        self.cast_vote("0xAlice", "Prop 1: Raise fees", small_votes, 100)
        
        print("\n--- Comparison ---")
        print("Token-based: Whale = 500,000 votes, Alice = 100 votes (5000x)")
        print("Quadratic: Whale = " + str(whale_votes) + " votes, Alice = " + str(small_votes) + " votes (5000x tokens but only " + str(round(whale_votes/small_votes, 1)) + "x votes)")

qd = QuadraticDAO()
qd.simulate()

Expected output:

0xWhale1 casts 707 votes for 'Prop 1: Raise fees' at cost 499849 tokens
0xAlice casts 10 votes for 'Prop 1: Raise fees' at cost 100 tokens

--- Comparison ---
Token-based: Whale = 500,000 votes, Alice = 100 votes (5000x)
Quadratic: Whale = 707 votes, Alice = 10 votes (5000x tokens but only 70.7x votes)

Delegation

Delegation lets token holders assign their voting power to a trusted representative without transferring tokens.

// Delegation.sol
// Voting delegation example
contract DelegatableDAO {
    mapping(address => address) public delegates;  // voter -> delegate
    mapping(address => uint256) public delegatedPower;  // delegate -> total power
    
    function delegate(address delegate) external {
        require(delegate != address(0), "Invalid delegate");
        require(delegate != msg.sender, "Cannot delegate to self");
        
        // Remove previous delegation
        address previousDelegate = delegates[msg.sender];
        if (previousDelegate != address(0)) {
            delegatedPower[previousDelegate] -= token.balanceOf(msg.sender);
        }
        
        // Set new delegate
        delegates[msg.sender] = delegate;
        delegatedPower[delegate] += token.balanceOf(msg.sender);
    }
    
    function votingPower(address voter) public view returns (uint256) {
        address delegate = delegates[voter];
        if (delegate == address(0)) {
            return token.balanceOf(voter);  // Self-vote
        }
        return 0;  // Power transferred to delegate
    }
}

On-Chain vs Off-Chain Voting

FeatureOn-Chain (Compound)Off-Chain (Snapshot)
Gas costHigh (each vote = tx)Free (signed message)
ExecutionAutomatic via smart contractManual (multisig executes)
SecurityHigh (immutable)Medium (relies on signers)
SpeedSlow (block times)Instant
Best forProtocol upgrades, parameter changesSignaling, community sentiment

Proposal Lifecycle


flowchart TB
  A[1. Ideation
Forum Discussion] --> B[2. Temperature Check
Snapshot Poll] B --> C{Community
Support?} C -->|No| D[Rejected] C -->|Yes| E[3. Formal Proposal
On-Chain Submission] E --> F[4. Voting Period
3-7 Days] F --> G{Votes Pass
Quorum Met?} G -->|No| H[Defeated] G -->|Yes| I[5. Timelock
2-7 Day Delay] I --> J[6. Execution
Multisig / Contract Call] J --> K[7. Post-Mortem
Results & Analysis]

Treasury Management with Gnosis Safe

DAOs manage millions in crypto through multisig wallets. Gnosis Safe is the industry standard:

// treasury_management.js
// Gnosis Safe interaction
import Safe from '@safe-global/protocol-kit';
import { ethers } from 'ethers';

async function manageTreasury() {
  // Connect to Gnosis Safe
  const safe = await Safe.init({
    provider: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
    signer: '0xYourSignerAddress',
    safeAddress: '0xDAO_Safe_Address',
  });
  
  // Check Safe configuration
  const config = await safe.getOwners();
  const threshold = await safe.getThreshold();
  console.log('Safe owners:', config);
  console.log('Required confirmations:', threshold.toString());
  
  // Create a transaction (requires 3/5 signatures)
  const transaction = {
    to: '0xRecipient',
    value: ethers.parseEther('100.0').toString(),
    data: '0x',
  };
  
  const safeTransaction = await safe.createTransaction({ transactions: [transaction] });
  const safeTxHash = await safe.getTransactionHash(safeTransaction);
  
  // Sign with current owner
  const signature = await safe.signTransaction(safeTransaction);
  console.log('Transaction hash:', safeTxHash);
  console.log('Waiting for', threshold.toString() - 1, 'more signatures...');
  
  // Execute when threshold reached
  const executeResponse = await safe.executeTransaction(safeTransaction);
  console.log('Executed:', executeResponse.transactionHash);
}

// Treasury diversification recommendations:
// 60% Stablecoins (USDC, USDT) — operational budget
// 25% Blue-chip (ETH, BTC) — long-term holdings
// 10% Protocol-owned liquidity — DeFi yields
// 5% Strategic investments — ecosystem grants

Notable DAOs

DAOPurposeGovernance TokenKey Feature
MakerDAODAI stablecoinMKRExecutive voting + governance polls
UniswapDEX protocolUNIDelegation + Snapshot signaling
AaveLending protocolAAVESafety module + treasury diversification
ENSDomain namesENSToken-weighted voting, community fund
Nouns DAONFT + treasuryNouns (NFT)Daily auctions, 10% treasury for builders

Common DAO Governance Mistakes

1. Low Quorum Requirements

A quorum of 1% means a small coordinated group can pass any proposal. Set quorum to 5-20% of circulating supply. Compound’s quorum of 4% is considered minimum viable.

2. Short Voting Periods

A 24-hour voting window excludes holders in different time zones and favors bots. Use 3-7 days. Longer periods increase thoughtful participation.

3. Whale Dominance (One-Token-One-Vote)

Without quadratic voting or delegation, a single holder with 51% controls the DAO. Use quadratic voting for sentiment-heavy votes and delegation to distribute power.

4. No Timelock

Without a timelock, a malicious proposal executes immediately. The timelock (2-7 days) gives holders time to exit or organize opposition. Compound uses a 2-day timelock.

5. Treasury Without Diversification

A DAO treasury 100% in its own token crashes when the token price drops. The DAO can’t fund operations. Diversify into stablecoins, ETH, and BTC.

6. Sybil Attacks in Off-Chain Voting

Without Sybil resistance, one person creates 100 wallets and votes 100 times. Use Gitcoin Passport, Proof of Humanity, or quadratic voting that makes Sybil attacks expensive.

7. Executing Without Testing

A governance proposal that calls transferOwnership to the wrong address can lock the treasury forever. Test proposals on a testnet fork first using Hardhat mainnet forking.

Practice Questions

1. What problem does quadratic voting solve compared to one-token-one-vote?

One-token-one-vote gives a whale with 51% of tokens complete control. Quadratic voting makes each additional vote cost quadratically more, so a whale with 1000x more tokens gets only ~31x more voting power.

2. How does delegation improve DAO governance?

Delegation lets token holders who lack time or expertise assign their voting power to trusted representatives. This increases participation (delegates vote on behalf of many holders) and creates accountable representatives.

3. What is the purpose of a timelock in governance proposals?

A timelock delays execution of approved proposals by 2-7 days. This gives token holders time to review the proposal, exit their positions if they disagree, or organize opposition before changes take effect.

4. How does Snapshot enable gasless voting?

Snapshot uses off-chain signatures instead of on-chain transactions. Voters sign a message (EIP-712 typed data) with their wallet. The signature is stored off-chain. Snapshot checks voting power at a specific block using token balances.

5. Challenge: Design a governance system for a protocol DAO with 100M token supply, 5000 holders, and $50M treasury. Include quorum, voting period, delegation, treasury diversification, and emergency pause mechanism.

Use token-weighted voting with quorum at 4% (4M tokens), 5-day voting period, delegation via OpenZeppelin’s governance module. Treasury: 60% USDC, 20% ETH, 10% protocol-owned liquidity, 10% grants. Emergency pause via 3/5 multisig with 48-hour timelock. Use Snapshot for signaling votes and on-chain execution for parameter changes.

Mini Project: Governance Simulator

# governance_sim.py
# Simulate a full governance proposal lifecycle
from datetime import datetime, timedelta
import random

class GovernanceProposal:
    def __init__(self, title: str, description: str, proposer: str):
        self.title = title
        self.description = description
        self.proposer = proposer
        self.created = datetime.now()
        self.voting_start = self.created + timedelta(days=2)
        self.voting_end = self.voting_start + timedelta(days=5)
        self.executed = False
        self.passed = False
        self.for_votes = 0
        self.against_votes = 0
        self.status = "Discussion"

class DAOSimulator:
    def __init__(self, total_supply: int, quorum_pct: float = 0.04):
        self.total_supply = total_supply
        self.quorum = int(total_supply * quorum_pct)
        self.holders = {}
        self.proposals = []
    
    def add_holder(self, address: str, tokens: int):
        self.holders[address] = tokens
    
    def create_proposal(self, title: str, proposer: str):
        prop = GovernanceProposal(title, "Detailed description...", proposer)
        self.proposals.append(prop)
        print(f"[{prop.created.strftime('%Y-%m-%d %H:%M')}] Proposal created: '{title}' by {proposer[:8]}")
        return prop
    
    def simulate_vote(self, proposal: GovernanceProposal):
        print(f"\n--- Voting on: {proposal.title} ---")
        print(f"Period: {proposal.voting_start.strftime('%m/%d')} - {proposal.voting_end.strftime('%m/%d')}")
        print(f"Quorum required: {self.quorum:,} tokens\n")
        
        for voter, tokens in self.holders.items():
            if random.random() < 0.4:  # 40% participation
                support = random.random() < 0.65  # 65% support rate
                if support:
                    proposal.for_votes += tokens
                    print(f"  {voter[:8]}: {tokens:>8,} tokens -> FOR")
                else:
                    proposal.against_votes += tokens
                    print(f"  {voter[:8]}: {tokens:>8,} tokens -> AGAINST")
        
        total_votes = proposal.for_votes + proposal.against_votes
        quorum_met = total_votes >= self.quorum
        majority = proposal.for_votes > proposal.against_votes
        proposal.passed = quorum_met and majority
        
        print(f"\nResults:")
        print(f"  For: {proposal.for_votes:,}")
        print(f"  Against: {proposal.against_votes:,}")
        print(f"  Total votes: {total_votes:,} ({total_votes/self.total_supply*100:.1f}% of supply)")
        print(f"  Quorum met: {'YES' if quorum_met else 'NO'}")
        print(f"  Proposal {'PASSED' if proposal.passed else 'DEFEATED'}")
        
        return proposal.passed

sim = DAOSimulator(total_supply=10_000_000, quorum_pct=0.04)

sim.add_holder("0xDAO_Treasury", 2_000_000)
sim.add_holder("0xTeam_Vesting", 1_500_000)
sim.add_holder("0xInvestor_A", 1_000_000)
sim.add_holder("0xInvestor_B", 500_000)
for i in range(500):
    sim.add_holder(f"0xHolder_{i:04d}", random.randint(100, 10000))

prop = sim.create_proposal("Upgrade Protocol to v2", "0xTeamLead")
sim.simulate_vote(prop)

Expected output:

[2026-06-20 12:00] Proposal created: 'Upgrade Protocol to v2' by 0xTeamLead

--- Voting on: Upgrade Protocol to v2 ---
Period: 06/22 - 06/27
Quorum required: 400,000 tokens

  0xDAO_Treasury:  2,000,000 tokens -> FOR
  0xTeam_Vesting:  1,500,000 tokens -> FOR
  ...

Results:
  For: 3,450,000
  Against: 1,200,000
  Total votes: 4,650,000 (46.5% of supply)
  Quorum met: YES
  Proposal PASSED

Related Concepts

FAQ

What is the difference between on-chain and off-chain DAO voting?
On-chain voting records every vote as a transaction (costs gas, but results execute automatically). Off-chain voting uses signed messages stored on Snapshot (gas-free, but execution requires a multisig or manual action).
How does quadratic voting prevent whale dominance?
Each additional vote costs quadratically more. A voter with 10,000 tokens can cast 100 votes (10,000 = 100^2). A voter with 1,000,000 tokens can cast 1,000 votes (1,000^2 = 1,000,000). The whale has 100x the tokens but only 10x the voting power.
What is a quorum and why is it important?
Quorum is the minimum number of votes required for a proposal to be valid. Without quorum, a proposal could pass with 2 votes out of 10,000 holders. Quorum ensures sufficient community participation.
How do DAOs manage their treasuries?
DAOs use multisig wallets (Gnosis Safe) requiring M-of-N signatures for transactions. Treasury funds are diversified across stablecoins, ETH, and yield-bearing protocols. Regular audits and spending limits prevent misuse.
What are some notable DAOs I should study?
MakerDAO (stablecoin governance), Uniswap (DEX protocol), Aave (lending), ENS (domain names), and Nouns DAO (NFT + builder grants). Each has different governance mechanisms worth studying.

What’s Next

You now understand DAO governance including token voting, quadratic voting, delegation, and treasury management. Next, explore DeFi protocols that DAOs govern and smart contract development to build your own governance contracts.

  • Practice daily — Join a Snapshot space and participate in a signal vote
  • Build a project — Deploy a SimpleDAO contract on Sepolia with a mock token and test the full proposal lifecycle
  • Explore further — Study MakerDAO’s governance framework and how it manages the DAI stablecoin

Remember: every expert was once a beginner. Keep governing!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro