Skip to content

DAOs: Decentralized Autonomous Organizations Explained

DodaTech Updated 2026-06-22 8 min read

In this tutorial, you'll learn DAOs including governance tokens, voting mechanisms, treasury management, and building a DAO with smart contracts for community-owned organizations. Why it matters: DAOs represent a new paradigm for human coordination, with over $20 billion in treasury assets managed by communities without centralized leadership, from investment clubs to protocol governance. By the end, you'll build your own DAO from scratch.

A DAO (Decentralized Autonomous Organization) is an organization governed by smart contracts and token-holder voting, where decisions are executed automatically without centralized management.

How DAOs Work

DAOs combine governance tokens, voting contracts, and treasury management to enable decentralized decision-making.

flowchart TD
  subgraph "DAO Structure"
    T[Governance Token]
    V[Voting Contract]
    F[Treasury]
    E[Execution]
  end
  
  subgraph "Participants"
    M[Members]
    P[Proposers]
    D[Delegates]
  end
  
  M -->|Hold tokens| T
  P -->|Create proposal| V
  V -->|Vote with tokens| M
  D -->|Vote on behalf| M
  V -->|Approved| E
  E -->|Execute| F
  F -->|Fund| P[Projects]
  F -->|Rewards| M
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract SimpleDAO is ReentrancyGuard {
    using SafeERC20 for IERC20;
    
    struct Proposal {
        uint256 id;
        address proposer;
        string description;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 deadline;
        bool executed;
        bool passed;
        address[] targets;
        uint256[] values;
        bytes[] calldatas;
    }
    
    IERC20 public governanceToken;
    uint256 public proposalCount;
    uint256 public constant VOTING_PERIOD = 7 days;
    uint256 public constant QUORUM = 4; // 4% of total supply
    
    mapping(uint256 => Proposal) public proposals;
    mapping(uint256 => mapping(address => bool)) public hasVoted;
    
    event ProposalCreated(uint256 indexed id, address indexed proposer, string description);
    event Voted(uint256 indexed id, address indexed voter, bool support, uint256 weight);
    event ProposalExecuted(uint256 indexed id);
    
    constructor(address _token) {
        governanceToken = IERC20(_token);
    }
    
    function propose(string calldata _description, address[] calldata _targets, uint256[] calldata _values, bytes[] calldata _calldatas) external {
        require(_targets.length > 0, "Empty targets");
        
        proposalCount++;
        proposals[proposalCount] = Proposal({
            id: proposalCount,
            proposer: msg.sender,
            description: _description,
            forVotes: 0,
            againstVotes: 0,
            deadline: block.timestamp + VOTING_PERIOD,
            executed: false,
            passed: false,
            targets: _targets,
            values: _values,
            calldatas: _calldatas
        });
        
        emit ProposalCreated(proposalCount, msg.sender, _description);
    }
    
    function vote(uint256 _proposalId, bool _support) external {
        Proposal storage prop = proposals[_proposalId];
        require(block.timestamp < prop.deadline, "Voting ended");
        require(!hasVoted[_proposalId][msg.sender], "Already voted");
        
        uint256 weight = governanceToken.balanceOf(msg.sender);
        require(weight > 0, "No voting power");
        
        hasVoted[_proposalId][msg.sender] = true;
        
        if (_support) {
            prop.forVotes += weight;
        } else {
            prop.againstVotes += weight;
        }
        
        emit Voted(_proposalId, msg.sender, _support, weight);
    }
    
    function execute(uint256 _proposalId) external nonReentrant {
        Proposal storage prop = proposals[_proposalId];
        require(block.timestamp >= prop.deadline, "Voting not ended");
        require(!prop.executed, "Already executed");
        
        uint256 totalSupply = governanceToken.totalSupply();
        uint256 totalVotes = prop.forVotes + prop.againstVotes;
        
        require(totalVotes >= (totalSupply * QUORUM) / 100, "Quorum not met");
        require(prop.forVotes > prop.againstVotes, "Proposal failed");
        
        prop.executed = true;
        prop.passed = true;
        
        for (uint256 i; i < prop.targets.length; i++) {
            (bool success, ) = prop.targets[i].call{value: prop.values[i]}(prop.calldatas[i]);
            require(success, "Action failed");
        }
        
        emit ProposalExecuted(_proposalId);
    }
}

Expected behavior: Token holders create proposals with executable actions. Others vote with their token balance. After 7 days, if quorum is met and forVotes > againstVotes, anyone can execute the proposal's actions.

Governance Tokens

Governance tokens represent voting power in a DAO. They can be earned, bought, or distributed.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

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

contract GovernanceToken is ERC20, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
    uint256 public constant MAX_SUPPLY = 100_000_000 * 10**18;
    
    constructor() ERC20("GovernanceToken", "GOV") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _mint(msg.sender, 10_000_000 * 10**18); // 10% to founder
    }
    
    function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
        require(totalSupply() + _amount <= MAX_SUPPLY, "Exceeds max supply");
        _mint(_to, _amount);
    }
    
    // Override transfer to add voting checkpoint logic
    function _update(address from, address to, uint256 value)
        internal
        override
    {
        super._update(from, to, value);
        // In production, update voting delegation checkpoints here
    }
}

Delegation and Voting Power

Most DAOs use delegation where token holders can assign their voting power to a representative.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract VotingDelegation {
    struct Checkpoint {
        uint256 fromBlock;
        uint256 votes;
    }
    
    mapping(address => address) public delegates;
    mapping(address => mapping(uint256 => Checkpoint)) public checkpoints;
    mapping(address => uint256) public numCheckpoints;
    
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    
    function delegate(address _delegatee) external {
        address previous = delegates[msg.sender];
        delegates[msg.sender] = _delegatee;
        
        // Move voting power
        uint256 amount = getVotes(msg.sender); // Current votes
        _moveDelegates(previous, _delegatee, amount);
        
        emit DelegateChanged(msg.sender, previous, _delegatee);
    }
    
    function _moveDelegates(address _from, address _to, uint256 _amount) internal {
        if (_from != address(0) && _from != _to) {
            uint256 fromIndex = numCheckpoints[_from];
            Checkpoint storage fromCp = checkpoints[_from][fromIndex];
            fromCp.fromBlock = block.number;
            fromCp.votes = getVotes(_from) - _amount;
            numCheckpoints[_from] = fromIndex + 1;
        }
        
        if (_to != address(0) && _to != _from) {
            uint256 toIndex = numCheckpoints[_to];
            Checkpoint storage toCp = checkpoints[_to][toIndex];
            toCp.fromBlock = block.number;
            toCp.votes = getVotes(_to) + _amount;
            numCheckpoints[_to] = toIndex + 1;
        }
    }
    
    function getVotes(address _account) public view returns (uint256) {
        // Returns voting power at current block
        uint256 nCheckpoints = numCheckpoints[_account];
        if (nCheckpoints == 0) return 0;
        return checkpoints[_account][nCheckpoints - 1].votes;
    }
    
    function getPastVotes(address _account, uint256 _blockNumber) public view returns (uint256) {
        // Binary search for checkpoint at or before _blockNumber
        uint256 nCheckpoints = numCheckpoints[_account];
        if (nCheckpoints == 0) return 0;
        
        uint256 low;
        uint256 high = nCheckpoints - 1;
        
        while (low < high) {
            uint256 mid = (low + high + 1) / 2;
            if (checkpoints[_account][mid].fromBlock <= _blockNumber) {
                low = mid;
            } else {
                high = mid - 1;
            }
        }
        
        return checkpoints[_account][low].votes;
    }
}

Expected behavior: Users delegate their voting power to a representative. The representative's votes include all delegated tokens. Snapshot-based voting ensures votes are counted at proposal creation time, preventing double-voting.

Treasury Management

DAOs need secure treasury management with multisig requirements and streaming payments.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract DAOTreasury {
    address public dao;
    uint256 public constant MIN_EXECUTION_DELAY = 2 days;
    
    mapping(bytes32 => bool) public executed;
    
    modifier onlyDAO() {
        require(msg.sender == dao, "Not DAO");
        _;
    }
    
    function execute(address _target, uint256 _value, bytes calldata _data) external onlyDAO {
        bytes32 hash = keccak256(abi.encodePacked(_target, _value, _data));
        require(!executed[hash], "Already executed");
        executed[hash] = true;
        
        (bool success, ) = _target.call{value: _value}(_data);
        require(success, "Execution failed");
    }
    
    // Stream ETH to a recipient over time
    function createStream(address _recipient, uint256 _amountPerSecond) external onlyDAO {
        // Vesting/streaming logic
    }
}

Building a DAO Dashboard

Create a frontend to interact with the DAO.

import { ethers } from "ethers";

class DAOClient {
  constructor(daoAddress, tokenAddress, provider) {
    this.dao = new ethers.Contract(daoAddress, DAO_ABI, provider);
    this.token = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
  }
  
  async getProposals() {
    const count = await this.dao.proposalCount();
    const proposals = [];
    
    for (let i = 1; i <= count; i++) {
      const prop = await this.dao.proposals(i);
      proposals.push({
        id: prop.id.toString(),
        proposer: prop.proposer,
        description: prop.description,
        forVotes: ethers.formatEther(prop.forVotes),
        againstVotes: ethers.formatEther(prop.againstVotes),
        deadline: new Date(Number(prop.deadline) * 1000),
        executed: prop.executed,
        passed: prop.passed,
      });
    }
    
    return proposals;
  }
  
  async getUserPower(address) {
    const balance = await this.token.balanceOf(address);
    const delegated = await this.dao.delegates(address);
    
    return {
      balance: ethers.formatEther(balance),
      isDelegating: delegated !== address,
      delegate: delegated,
    };
  }
  
  async createProposal(description, targets, values, calldatas) {
    const tx = await this.dao.propose(description, targets, values, calldatas);
    const receipt = await tx.wait();
    
    // Parse event to get proposal ID
    const event = receipt.logs.find(log => {
      try {
        return this.dao.interface.parseLog(log).name === "ProposalCreated";
      } catch { return false; }
    });
    
    return this.dao.interface.parseLog(event).args.id;
  }
}

// Usage
const dao = new DAOClient("0x...", "0x...", provider);
const proposals = await dao.getProposals();
console.log(`Active proposals: ${proposals.filter(p => !p.executed).length}`);
// Expected: Active proposals: 3

Common Errors and Misunderstandings

1. Low Participation

Most DAOs suffer from voter apathy. Without participation, a small minority controls decisions. Implement delegation and incentivize voting.

2. Whale Dominance

Large token holders can dominate voting. Consider quadratic voting, conviction voting, or vote-weight caps.

3. Governance Attacks

Attackers can accumulate tokens to pass malicious proposals. Implement timelocks, multi-sig overrides, and gradual voting power.

4. Treasury Security

DAOs with large treasuries are targets. Use multi-sig for treasury management, timelocks for all executions, and regular security reviews.

DAOs face unclear regulatory status. Consider legal wrappers (DAO LLC, UNA) and consult legal counsel before launching.

Practice Questions

  1. What are the core components of a DAO? Governance tokens, voting contracts, treasury management, and execution mechanisms. Proposals are created, voted on by token holders, and executed automatically.

  2. How does delegation work in DAOs? Token holders assign their voting power to a delegate. The delegate votes on their behalf. Delegation can be changed at any time and is tracked via on-chain checkpoints.

  3. What is quorum in DAO voting? Quorum is the minimum number of votes required for a proposal to be valid. It is usually expressed as a percentage of total token supply. Without quorum, a small minority could pass proposals.

  4. How do DAOs prevent governance attacks? Through timelocks (delayed execution), multi-sig requirements for sensitive actions, vote-weight caps, flash loan prevention, and gradual voting power accumulation.

  5. What is a governance token used for? Governance tokens represent voting power in a DAO. Holders can vote on proposals, delegate to others, and sometimes earn rewards for participation.

Challenge

Build a complete DAO system with Solidity including a governance token with delegation checkpoints (like Compound's COMP), a timelock controller (4-hour delay), a governor contract supporting multiple proposal types, and a treasury with multi-sig override. Write comprehensive tests in Hardhat.

Real-World Task

Create a community DAO for managing a shared NFT collection using OpenZeppelin governance contracts. Members stake NFTs to receive governance tokens, propose NFT acquisitions or sales, vote on proposals, and the DAO treasury executes approved transactions. Deploy on Sepolia and test with a React dashboard.

Frequently Asked Questions

Are DAOs legally recognized?

Legal recognition varies by jurisdiction. Wyoming (USA), Delaware (USA), and Switzerland have DAO-specific legislation. Most DAOs operate under a legal wrapper (LLC, foundation, association) to limit member liability.

How do DAOs make decisions?

Most DAOs use token-weighted voting: each token equals one vote. Proposals require a minimum number of votes (quorum) and majority approval. Some DAOs use quadratic voting, conviction voting, or holographic consensus for better outcomes.

What is the problem with low voter turnout?

Low turnout makes DAOs vulnerable to minority control. A few active voters can pass proposals against the interest of the silent majority. Solutions include delegation, voting rewards, and minimum participation thresholds.

Next Steps

After building DAOs, explore ENS for human-readable addresses, then dive into cross-chain governance with Layer 2 DAOs.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro