第一个存款的人为什么危险:份额通胀攻击的机制与防线 图 1
第一个存款的人为什么危险:份额通胀攻击的机制与防线 · 图 1

收益金库类协议里,你的存款通常不直接记成资产数量,而是换成一种内部凭证:份额。存入时你拿到份额,收益来了之后每个份额对应的资产变多,赎回时按当时比例换回资产。这个结构优雅又省 gas,但它有一个对「先来者」极其有利的先天不对称,也就是俗称的份额通胀攻击。先讲清楚它为什么成立,再讲协议端和用户端各自的防线。

机制核心在取整。假设一个金库刚上线,第一个存款人存入一笔资产,协议按「资产除以份额」记账,第一笔通常一比一铸造份额。攻击者此时做一件事:往金库里直接转账一大笔资产而不领取份额。这笔捐赠让金库总资产瞬间变大,而总份额几乎没变,每个份额对应的资产价值被人为抬高。接下来受害者入场:想存入一小笔资产的人,能换到的份额数量等于「存款额除以每份额资产价」,向下取整。当每份额资产价被推得足够高,小额存款取整后可能得到零份额——钱进了池子,凭证一份没拿到。之后这些资产随收益分配流程摊给所有持份者,攻击者因为持有几乎全部份额,把整池几乎全部领走。捐赠不是白花的:它是买断「下一个存款者的取整损失」的成本,只要金库后续有多笔小额流入,攻击就划算。

这个漏洞不是某个项目的代码失误,而是「份额除以总资产」这套会计模型在余额可以被外部凭空注入时的固有性质。所以防线也分两层。协议层最常见的是虚拟资产:在记账公式里给分子分母各加一个固定常数,让第一笔真实存款之前就存在一个不为零的基数,取整损失被稀释到可忽略的水平;ERC4626 标准在讨论中广泛推荐这类处理,另一个思路是最小初始存款,让首存份额基数大到无法被一笔捐赠显著扭曲。用户层的判断则朴素得多:看这个金库是否新部署、首存是否已经发生、初始份额基数有多大。一个刚上线、流动性薄、首存金额很小的金库,等于把取整攻击的门槛放得最低。

还有一类变种值得单独记住:_donation 型攻击不需要等别人来存,攻击者自己就是受害者——如果你存入的代币在转账时会额外扣留一部分(即转账手续费代币),实际到账少于申报数量,在特定合约写法下你拿到的份额会按申报值计算,差额立刻被别人套利。所以把新资产接入金库前,核验它的转账行为与合约标准兼容性,是运营方而不是用户该做的事;用户的对应动作是避开刚上架、机制说明含糊的小金库。

最后给一个可以自己动手核对的清单。第一,去区块浏览器查这个金库合约的部署时间,越新风险窗口越大。第二,看总份额数:如果总份额与总储备都小得可怜,说明还没有健康的首存者群体。第三,看合约或文档里有没有写明虚拟份额、最小存款或其他取整保护,正式审计报告是否专门覆盖过首存者问题。第四,第一笔投入控制在自己能承受完全损失的额度内,观察一两个收益周期、确认份额与储备的兑换比例变化符合预期再加注。这些动作不能证明一个金库安全,但能过滤掉最典型的一类结构性陷阱。

值得补充的是,防御措施本身也要读实现而不是读名词。同样宣称「有虚拟份额」的两个金库,一个把常数加在足够大的量级、并在每笔存取里对称处理取整方向,另一个只在初始化时加了一次常数、后续仍按原始公式取整——前者真正稀释了攻击空间,后者只是给审计清单打了个勾。判断方法依然是行为观察而非文案:在测试网或小额试探里,用两笔数量级相差悬殊的存款先后入场,记录各自实得份额与理论值的偏差方向;如果小额存款的实得比例系统性偏低,那说明取整损失真实存在且归你承担,无论文档怎么写都应视为红旗。任何协议机制都不能保证本金不受损,存取决策请自行评估,本文全部内容不构成投资建议。

第一个存款的人为什么危险:份额通胀攻击的机制与防线 图 2
第一个存款的人为什么危险:份额通胀攻击的机制与防线 · 图 2