了解将Token作为投票权可能带来的威胁,了解oz的ERC20Snapshot and ERC20Votes背后的原理,以及作为开发者需要注意什么问题。
Token作为投票往往运用在DAO治理或是领取空投,通常根据用户持有的代币数量来分配投票权,这会带来什么潜在的问题和攻击手段呢?
为了防御这些问题,可以采取以下措施:
ERC20Snapshot库是ERC20的拓展,增加了各账户余额及总流通量的快照机制。如果涉及到根据账户ERC20余额进行分红、投票等业务可以使用该库,其可有效防御在不同地址间转账进行“一币多用”的攻击。
在一个快照横截面数据上进行分红、投票甚至是ERC20分叉都是最有效的解决方案。本库具有高效性,创建快照、快照上查询地址余额及总流通量的时间复杂度分别是O(1)
和O(log n)
。但快照功能的存在会增加ERC20发生转移时的gas成本。
阻止攻击者在一笔交易通过将token转移给多个智能合约来进行双重投票攻击。
攻击者可以利用闪电贷获取大量代币,进行快照获取大量投票权,再进行提案投票或是领取空投,可以通过damn-vulnerable-defi第六题selfie来加深理解,这是我的题解可供参考。
ERC20 Votes实际上是对ERC20 Snapshot功能的扩展,增加了其他治理相关的功能,不是为了解决了Snapshot的缺点而诞生的。
ERC20 Votes是标准ERC20代币的扩展,继承了ERC20、ERC6372和ERC5805的功能,专门设计用于处理投票权,它支持快照和委托投票权功能。实际的投票过程由治理合约管理,而不是ERC20 Votes本身。
ERC5805相当于是interface。下面是我们需要关注的几个函数接口,主要与委托投票以及快照有关。
delegate(address delegatee)
这个函数允许msg.sender将其投票权委托给delegatee(代币并不会转移),delegatee将代表msg.sender进行投票。委托可以随时通过再次调用delegate函数并传入不同的delegatee或address(0)来更改或撤销。代币持有者只能选择全部委托或不委托,不存在部分委托。一个地址必须先委托给自己,其投票权才会被计入。这是为了提高Gas效率。
delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)
函数delegateBySig允许用户通过无Gas交易委托投票权,并由另一个账户支付Gas费用并执行交易。expiring设置委托有效的时间,v、r和s是椭圆曲线数字签名的组成部分。预期签名为EIP 712格式。合约内部会递增每个地址的nonce,以防止重放攻击。
getPastVotes(account, timepoint) →防止双重投票
与ERC20 snapshot不同,快照不会由调用快照函数的地址触发。当铸币、销毁、转账、有人委托其投票权这四件事发生时,才会为每个账户触发快照。快照会将包含投票权和时间戳的结构体附加到存储用户投票权历史记录的数组中。ERC20 Votes会对时间戳进行二分搜索,以找到timepoint后的最早检查点,并返回该时刻的投票权。
与ERC20 snapshot不同的是,没有“全局”快照ID,通过时间戳或区块号作为“timepoint”去查询快照。
还需要关注两个event,分别是DelegateChange
和DelegateVotesChanged
。
合约可以使用block.timestamp,block.number或这些全局变量的单调增加的函数来记录检查点。ERC6372是一个针对检查点的标准,允许合约查询合约使用的“时钟”类型。
clock() 这个函数返回一个uint48,可能是区块号或区块时间戳或其他表征检查点的clock。
CLOCK_MODE() EIP规定这种全大写的命名,返回字符串,即时钟使用的单位。
ERC20 Votes:有明确的时间概念,在每次投票权相关的事件(如委托或转账)发生时,记录当前的时间戳或区块号。以后可以根据具体时间点查询历史投票权。 ERC20 Snapshot:使用递增的ID作为快照标识,这些ID随着时间推移自动增加,作为计数器的副产品。这种方式也反映了时间的变化,但没有明确的时间戳或区块号记录。
ERC20 Votes:记录的是投票权。每个地址在特定时间点上的投票权会被快照保存。这对于治理和投票非常重要,因为投票权可以通过委托等方式进行变化。 ERC20 Snapshot:记录的是代币余额。它跟踪每个地址在特定时间点上的代币余额,对于需要追踪资产分布情况的场景非常合适。
ERC20 Votes:在每次委托或转账时自动更新检查点。 ERC20 Snapshot:需要显式调用“_snapshot()”函数来更新检查点。
ERC20 Votes:适用于需要委托投票权的场景。它可以灵活处理投票权的变化,并在每次委托或转账时自动更新检查点。这对于去中心化自治组织(DAO)的治理、投票和选举等场景更合适。 ERC20 Snapshot:适用于需要记录和追踪代币余额的场景。它能够提供历史余额的准确记录,对于资产管理、空投和奖励分配等场景更合适。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!