使用投票委托

在去中心化自治组织(DAO)和其他链上治理协议中,投票是核心环节。传统的治理模式要求每个代币持有者都发起一笔独立的链上交易来投出自己的一票。

这种模式存在一个显著的痛点:

  • 高昂的 Gas 成本:如果一个提案需要 1000 个成员参与投票,那就意味着需要执行 1000 次独立的 vote() 交易,从而产生 1000 笔交易的 Gas 费用。当网络拥堵时,这笔总成本会变得极其高昂,严重阻碍了社区成员参与治理的积极性。

  • 低参与率:由于 Gas 成本高昂,许多持有少量代币的用户可能会因为“投票成本高于潜在收益”而放弃投票,导致治理中心化于巨鲸用户。

ERC20Votes 的委托投票(Vote Delegation)

为了解决上述问题,OpenZeppelin 提出了以 ERC20Votes 为核心的委托投票标准。其核心思想是将代币的所有权与投票权进行解耦,并通过委托(Delegation)来汇集投票权,从而大幅减少链上交易的数量。该机制主要依赖于以下几个关键功能:

  1. 投票权激活 (`delegate`):

    • 一个用户仅仅持有代币,并不意味着他拥有了可用的投票权。他必须首先调用一次 delegate(address representative) 函数来“激活”或“登记”他的投票权。

    • 他可以将投票权委托给自己 (delegate(address(this))),也可以委托给社区中任何他信任的代表。

    • 这是一个一次性的主动操作。一旦完成委托,除非想更换代表,否则无需再次执行。

  2. 投票权快照 (`getPastVotes`):

    • 为了防止在提案发起后通过买入代币来操-纵投票,系统采用了“快照”机制。当一个提案被创建时,合约会记录下当前的区块号。

    • 在投票时,系统会使用 getPastVotes(address, blockNumber) 函数来查询投票者在提案创建那个时刻所拥有的投票权,而不是投票时的实时投票权。

    • 投票权历史的更新只在 delegate() 或被委托的代币发生转移时触发,而不是在每一次普通的 transfer 时都触发,这正是其 Gas 高效的关键。

  3. 投票权汇集:

    • 这是节省 Gas 的核心。一个社区代表可以接受大量用户的投票权委托。例如,1000 个用户可以将他们的投票权全部委托给 10 个社区领袖。

    • 当一个新提案出现时,不再需要 1000 个人去分别投票,而只需要这 10 个代表去投票。每个代表的一票,实际上代表了其背后所有委托者的总票数。

测试

文件位置:

src/patterns/Votes.sol
test/patterns/Votes.t.sol

测试结果:

forge test --gas-report --mt test_votes_ -vvv --optimize

总结

通过采用 ERC20Votes 的委托投票机制,可以将原本需要 N 次独立投票的昂贵过程,优化为只需 M 次代表投票的经济过程(其中 M 可以远小于N)。这种模式极大地降低了链上治理的总 Gas 成本,清除了小额持币者参与治理的障碍,从而有助于提升去中心化治理的参与度和健壮性。

实施建议:

  1. 采用 OpenZeppelin 标准合约:直接使用或继承 OpenZeppelin 的 ERC20Votes 合约是实现此模式最安全、最高效的方式。如我们的 GovernanceToken所示,通过正确的多重继承可以轻松集成 ERC20, ERC20Permit 和 ERC20Votes 的功能。

  2. 加强前端引导:治理应用的前端(UI)至关重要。必须清晰地引导用户完成一次性的 delegate()操作,并向他们解释为什么需要这一步,以及他们可以将票委托给谁。

  3. 鼓励代表文化:积极建设社区代表(Delegates)文化。通过排行榜、徽章等方式激励那些知识渊博、积极参与的社区成员成为代表,并鼓励其他用户将票委托给他们,从而最大化地发挥该机制的 Gas 节省优势。

对应源码

本文配套代码固定到提交 8960383;Gas 数据会随编译器、优化器参数和 EVM 版本变化。