DAO2.0 从理论到代码:新一代去中心化治理实战

5 阅读9分钟

前言

继上一篇 DAO 基础理论与代码落地的讲解,本文继续深入DAO2.0核心理论,并带来可直接落地的代码实践,带你完成从基础到进阶的技术跨越。

概述

DAO 1.0 与 DAO 2.0 的核心区别在于从“初级的自动化投票”进化到“复杂的治理与流动性管理”。DAO 1.0 解决了基础的组织去中心化问题,而 DAO 2.0 致力于解决治理低效、流动性不可持续以及跨组织协作等深层次挑战

DAO1.0 vs DAO2.0关键差异

1. 核心定义与重心

  • DAO 1.0(基础自动化) :主要关注通过智能合约实现组织的初步运行,如提案、投票和国库资金拨付。其核心是“代码即法律”,强调规则的透明度和不可篡改性。
  • DAO 2.0(管理与演化) :重心转向“治理的治理”(Management of Management)。它不仅是规则的执行,更引入了算法自动适配、管理会计和实时审计等系统,使组织能根据外部环境(如市场波动、Gas 费变化)自动调整策略。 

2. 流动性与经济模型(DeFi 2.0 视角)

  • DAO 1.0:通常依赖流动性挖矿(Yield Farming)吸引外部资金。用户持有流动性,一旦奖励减少,资金便会迅速流失,导致“雇佣兵资本”问题。
  • DAO 2.0:引入了协议控制流动性(Protocol-Owned Liquidity, POL) 。组织通过债券等机制直接拥有自己的流动性,增强了财务稳定性,减少了对第三方投资者的依赖。 

3. 协作维度

  • DAO 1.0:侧重于 DAO 内部成员的交互,即解决一个组织内部如何达成共识。
  • DAO 2.0:开启了 DAO2DAO (D2D)  时代。研究和实践转向不同 DAO 之间的协作、跨链资产管理以及生态系统级别的资源整合。 

4. 技术与工具演进

  • DAO 1.0:受限于 Layer 1 的高昂 Gas 费和较低的吞吐量,治理决策成本高且响应慢。
  • DAO 2.0:利用 Layer 2 扩展方案、更复杂的智能合约架构(如 Hedera Smart Contracts 2.0)以及模块化治理工具,实现了更高的交易速度、数据精确度和参与自由度。

关键差异总结表

维度DAO 1.0DAO 2.0
治理重心基础投票与资金拨付动态自适应治理、算法审计
流动性控制参与者持有(不稳定)协议自身持有(POL,可持续)
协作范围内部成员交互跨组织 (DAO2DAO) 协作
解决痛点中心化权威问题治理效率低、流动性不稳、高成本
典型特征“代码即法律”“管理之上的管理”

DAO 1.0 vs 2.0 落地表现对比

1. 协议控制流动性 (Protocol-Owned Liquidity, POL)

这是 DAO 2.0 最早的爆发点(源于 DeFi 2.0 运动)。

  • 场景:DAO 不再发放高额代币奖励(挖矿)来租用流动性,而是通过债券(Bonds)机制用协议代币换取用户的 LP Token。
  • 落地案例OlympusDAO。它将流动性永久留在 DAO 金库(Timelock)中,使组织拥有极强的财务韧性,避免了“挖矿、提现、砸盘”的恶性循环。

2. DAO2DAO (D2D) 协同治理与资产互换

DAO 1.0 是个人与组织的交互,2.0 开启了组织间的商业协作。

  • 场景:两个 DAO 通过智能合约进行库藏资产互换(Token Swaps)、共同出资建立联合激励池,或建立跨协议的治理约束。
  • 落地案例PrimeDAO。它开发了专门用于 D2D 协作的工具(如 Joint Ventures),允许两个 DAO 共同管理一个子金库,实现生态捆绑。

3. 子 DAO (Sub-DAOs) 与模块化职能部门

解决大型 DAO 治理低效(所有人投所有票)的问题。

  • 场景:母 DAO 将预算和权限分配给专注特定职能的子 DAO(如审计组、开发组、风险组)。子 DAO 在授权范围内自主决策,母 DAO 保留最终否决权。
  • 落地案例MakerDAO (Endgame 计划) 。它拆分出多个“元子 DAO”(MetaDAOs),每个子 DAO 有独立的代币和治理逻辑,降低了主协议的治理负荷。

4. 自动化算法治理 (Algorithmic Governance)

将治理从“纯人为判断”升级为“算法触发”。

  • 场景:利用 Oracle(预言机)  监测链上指标。当某些参数(如稳定币脱锚、国库清算风险)达到阈值时,合约自动触发防御性提案或参数调整,无需等待漫长的人工投票。
  • 落地案例Lido 或 Aave。其风险控制模块可以在紧急状态下由算法自动暂停特定借贷市场,这种“实时响应”是 DAO 2.0 的标志。

5. 跨链治理 (Cross-chain Governance)

随着多链生态发展,DAO 必须具备跨链指挥权。

  • 场景:治理提案在以太坊主网发起并投票,执行结果通过跨链桥(如 LayerZero, Axelar)自动触发 Layer 2 或其他公链上的合约修改。
  • 落地案例Uniswap。它的治理依然在 L1,但可以通过跨链指令管理部署在 Polygon、Arbitrum 等链上的协议参数。

DAO 1.0 vs 2.0 落地表现对比

特性DAO 1.0 表现DAO 2.0 表现
金库管理闲置资产,仅用于拨款积极投资、POL、流动性管理
执行效率依赖多签,人工操作多时间锁自动化、算法触发执行
组织结构扁平化,决策拥堵层级化(子 DAO)、模块化治理
激励机制简单的代币奖励动态归属(Vesting)、基于贡献的声誉系统

智能合约落地全流程

智能合约

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import "@openzeppelin/contracts/governance/TimelockController.sol";
contract MyToken is ERC20, ERC20Permit, ERC20Votes {
    constructor() ERC20("Governance Token", "GT") ERC20Permit("Governance Token") {
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    // 解决 ERC20Permit 和 Governor(Nonces) 的冲突
    function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }

    function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Votes) {
        super._update(from, to, value);
    }
}
// 包装一下,让 Hardhat 能够识别并编译它
contract MyTimelock is TimelockController {
    constructor(
        uint256 minDelay,
        address[] memory proposers,
        address[] memory executors,
        address admin
    ) TimelockController(minDelay, proposers, executors, admin) {}
}
contract MyGovernor is 
    Governor, 
    GovernorSettings, 
    GovernorCountingSimple, 
    GovernorVotes, 
    GovernorVotesQuorumFraction, 
    GovernorTimelockControl 
{
    constructor(IVotes _token, TimelockController _timelock)
        Governor("MyDAO_2.0")
        GovernorSettings(1, 10, 0)
        GovernorVotes(_token)
        GovernorVotesQuorumFraction(4)
        GovernorTimelockControl(_timelock)
    {}

    // --- 核心修复:简化 override 列表,只保留基类 Governor ---

    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 view override(Governor, GovernorVotesQuorumFraction) returns (uint256) {
        return super.quorum(blockNumber);
    }

    function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) {
        return super.state(proposalId);
    }

    function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
        return super.proposalThreshold();
    }

    // 这里是报错的关键:简化为 override(Governor)
    function supportsInterface(bytes4 interfaceId) public view override(Governor) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    // V5 内部执行逻辑
    function _executeOperations(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) 
        internal override(Governor, GovernorTimelockControl) 
    {
        super._executeOperations(proposalId, targets, values, calldatas, descriptionHash);
    }

    function _queueOperations(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) 
        internal override(Governor, GovernorTimelockControl) returns (uint48) 
    {
        return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash);
    }

    function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) 
        internal override(Governor, GovernorTimelockControl) returns (uint256) 
    {
        return super._cancel(targets, values, calldatas, descriptionHash);
    }

    function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
        return super._executor();
    }

    function proposalNeedsQueuing(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (bool) {
        return super.proposalNeedsQueuing(proposalId);
    }
}

测试脚本

测试用例说明

  • 完成从提案发起、投票、进入时间锁到成功拨付资金的完整 2.0 流程
  • 赞成票未达标时提案应被拒绝
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import { network } from "hardhat";
import { keccak256, encodePacked, encodeFunctionData, decodeEventLog, zeroAddress } from 'viem';

describe("DAO 2.0 Governance Lifecycle (V5 + Viem)", function () {
    let token: any;
    let timelock: any;
    let governor: any;
    let publicClient: any;
    let testClient: any;
    let deployer: any, voter: any, proposer: any;

    beforeEach(async function () {
        const { viem } = await (network as any).connect();
        publicClient = await viem.getPublicClient();
        testClient = await viem.getTestClient();
        [deployer, voter, proposer] = await viem.getWalletClients();

        // --- 修复 1: 使用完全限定名 (Fully Qualified Name) 解决 HHE1001 ---
        token = await viem.deployContract("contracts/DAO2.0.sol:MyToken", []);
        
        // Timelock 通常在库里,如果没有重名可以直接用,稳妥起见也可以指定
         timelock = await viem.deployContract("contracts/DAO2.0.sol:MyTimelock", [
            0n, 
            [], 
            [], 
            deployer.account.address
        ]);

        governor = await viem.deployContract("contracts/DAO2.0.sol:MyGovernor", [
            token.address, 
            timelock.address
        ]);

        // --- 核心修复点:分配足够的代币以满足 Quorum (4%) ---
        // 假设总供应量是 1,000,000。4% 是 40,000。
        // 我们给 voter 分配 100,000 (10%) 确保绝对通过。
        const amount = 100000n * 10n ** 18n; 
        await token.write.transfer([voter.account.address, amount]);
        
        // 关键:必须在 delegate 后推进区块,Governor 才会记录快照
        await token.write.delegate([voter.account.address], { account: voter.account });
        
        // 给金库充钱(供提案拨付用)
        await token.write.transfer([timelock.address, amount]);

        // 推进区块以确保 delegate 产生的快照被 Governor 识别
        await testClient.mine({ blocks: 5 }); 
        
        // 4. 权限设置 (保持不变)
        const PROPOSER_ROLE = keccak256(encodePacked(['string'], ['PROPOSER_ROLE']));
        const EXECUTOR_ROLE = keccak256(encodePacked(['string'], ['EXECUTOR_ROLE']));
        await timelock.write.grantRole([PROPOSER_ROLE, governor.address]);
        await timelock.write.grantRole([EXECUTOR_ROLE, zeroAddress]);
    });

    it("应该完成从提案发起、投票、进入时间锁到成功拨付资金的完整 2.0 流程", async function () {
        const calldata = encodeFunctionData({
            abi: token.abi,
            functionName: 'transfer',
            args: [proposer.account.address, 100n * 10n ** 18n]
        });
        const description = "DAO 2.0: Grant Funding";
        const descHash = keccak256(encodePacked(['string'], [description]));

        // 1. Propose
        const txHash = await governor.write.propose([
            [token.address], [0n], [calldata], description
        ], { account: proposer.account });
        
        const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
        
        // --- 修复 2: 健壮的事件解码 (适配 V5 多个 Log 的情况) ---
        const event = decodeEventLog({
            abi: governor.abi,
            eventName: 'ProposalCreated',
            // 遍历 logs 找到对应的事件
            ...receipt.logs.find((log: any) => {
                try {
                    const decoded = decodeEventLog({ abi: governor.abi, ...log });
                    return decoded.eventName === 'ProposalCreated';
                } catch { return false; }
            })
        });
        const proposalId = (event.args as any).proposalId;

        // 2. Vote
        await testClient.mine({ blocks: 2 });
        await governor.write.castVote([proposalId, 1], { account: voter.account });

        // 3. Queue
        await testClient.mine({ blocks: 15 }); 
        const currentState = await governor.read.state([proposalId]);
        console.log("Proposal State after voting:", currentState); // 应该输出 4 (Succeeded)
        assert.equal(Number(currentState), 4, "提案应当成功通过,而不是 Defeated");
        await governor.write.queue([[token.address], [0n], [calldata], descHash]);

        // 4. Execute
        await governor.write.execute([[token.address], [0n], [calldata], descHash]);

        // 验证
        const balance = await token.read.balanceOf([proposer.account.address]);
        assert.equal(balance, 100n * 10n ** 18n, "资金应由 Timelock 拨付成功");
    });

    it("当赞成票未达标时提案应被拒绝 (Defeated)", async function () {
        const tx = await governor.write.propose([[token.address], [0n], ['0x'], "Fail Test"]);
        const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
        const event = decodeEventLog({
            abi: governor.abi, eventName: 'ProposalCreated',
            ...receipt.logs.find((log: any) => log.topics[0] === keccak256(encodePacked(['string'], ['ProposalCreated(uint256,address,address[],uint256[],string[],bytes[],uint256,uint256,string)'])))
        });
        const proposalId = (event.args as any).proposalId;

        await testClient.mine({ blocks: 2 });
        await governor.write.castVote([proposalId, 0], { account: voter.account });
        
        await testClient.mine({ blocks: 15 }); 
        const state = await governor.read.state([proposalId]);
        assert.equal(Number(state), 3, "状态应为 Defeated");
    });
});

部署脚本

// scripts/deploy.js
import { network, artifacts } from "hardhat";
async function main() {
  // 连接网络
  const { viem } = await network.connect({ network: network.name });//指定网络进行链接
  
  // 获取客户端
  const [deployer] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
 
  const deployerAddress = deployer.account.address;
   console.log("部署者的地址:", deployerAddress);
  // 加载合约
  const TokenArtifact = await artifacts.readArtifact("contracts/DAO2.0.sol:MyToken");
  const GovernorArtifact = await artifacts.readArtifact("contracts/DAO2.0.sol:MyGovernor");
  const TimelockArtifact = await artifacts.readArtifact("contracts/DAO2.0.sol:MyTimelock");
 const TokenHash = await deployer.deployContract({
    abi: TokenArtifact.abi,//获取abi
    bytecode: TokenArtifact.bytecode,//硬编码
    args: [],
  });
  const TokenReceipt = await publicClient.waitForTransactionReceipt({ hash: TokenHash });
  console.log("Token合约地址:", TokenReceipt.contractAddress);
  
  const TimelockHash = await deployer.deployContract({
    abi: TimelockArtifact.abi,//获取abi
    bytecode: TimelockArtifact.bytecode,//硬编码
    args: [0n, [], [], deployerAddress],
  });
  const TimelockReceipt = await publicClient.waitForTransactionReceipt({ hash: TimelockHash });
  console.log("Timelock合约地址:", TimelockReceipt.contractAddress);
  // 部署
  const GovernorHash = await deployer.deployContract({
    abi: GovernorArtifact.abi,//获取abi
    bytecode: GovernorArtifact.bytecode,//硬编码
    args: [TokenReceipt.contractAddress, TimelockReceipt.contractAddress],
  });
   const GovernorReceipt = await publicClient.waitForTransactionReceipt({ hash: GovernorHash });
   console.log("Governor合约地址:", GovernorReceipt.contractAddress);
}

main().catch(console.error);

结语

至此,本文已完成 DAO2.0 从理论到代码落地的全流程讲解,并系统对比了 DAO1.0 与 DAO2.0 的核心差异。希望能为大家理解与实践新一代 DAO 提供清晰参考。