存款前防范 ERC-4626 份额膨胀攻击,最有效的动作是在存款前确认这个金库是否已经设置了有效的"死份额"(dead shares)或虚拟份额/小数偏移保护。如果金库是全新的、totalSupply 为 0 或极小,且没有上述保护,你的存款可能在极端情况下被四舍五入为 0 份额,钱进了金库但份额归零,而攻击者用 1 wei 的初始份额赎走全部资产。
攻击的机制:为什么存款会变成"捐赠"
ERC-4626 金库用 totalAssets / totalSupply 决定份额价格。攻击者在金库为空或接近空时,先存入 1 wei 拿到 1 份额,然后直接向合约地址转账(不调用 deposit),让 totalAssets 暴增而 totalSupply 不变。此时每个份额的价格被推到极高。
你随后正常调用 deposit 存入一笔资产,金库计算你能拿到的份额数时,公式大致是 assets × totalSupply / totalAssets。因为 totalSupply 还是 1,totalAssets 已经被撑得很大,你的存款额除以一个巨大的分母,结果向下取整后可能是 0。你收到 0 份额,存款实质上变成了对金库的捐赠。攻击者用那 1 份额赎回,拿走包括你存款在内的全部余额。
OpenZeppelin 的文档量化过攻击成本:在没有小数偏移保护的情况下,如果受害者准备存入 u 个资产,攻击者只需要投入约 u+1 个资产就能完成攻击,收益大致等于受害者的全部存款。
存款前该查什么
查 totalSupply 和 totalAssets 的关系。 如果 totalSupply 极小(比如 1 wei 或个位数),而 totalAssets 已经很大,这个金库要么已经被攻击,要么保护机制有问题。正常金库在早期也应该有足够的份额供应来让四舍五入损失可忽略。OpenZeppelin 的指南指出,想将存款的舍入损失限制在 1% 以内,你至少需要收到 100 份份额;如果份额价格被推到极高,同样的存款只能拿到远少于 100 份的份额,风险就暴露了。
查金库是否使用了 OpenZeppelin 5.x 的虚拟份额机制。 OpenZeppelin 从 v4.9 起在 ERC-4626 实现中引入了可配置的虚拟资产和虚拟份额,通过 _decimalsOffset() 来设定。默认偏移量(0)已经能让攻击变得无利可图,更大的偏移量会让攻击成本高出几个数量级。这不是完全消除攻击,而是让攻击者从捐赠中捕获的价值被虚拟份额吸收,与其预期收益相抵,从而失去经济动机。如果你存的金库基于 OpenZeppelin 5.x 且没有覆写 _convertToShares 和 _convertToAssets,保护大概率存在。如果金库是 2023 年之前部署的、或基于旧版实现,需要格外小心。
查是否有死份额(dead shares)。 一些协议(如 Morpho Vault V2)要求金库在部署时向 0x...dEaD 地址铸造一批不可赎回的份额,为份额供应量设定一个基线,使膨胀攻击在经济上不可行。Morpho 文档给出了具体数量参考:18 位小数的资产至少需要 1e9 份死份额,6 位小数的资产需要 1e18 份。你可以在区块浏览器上查看金库合约的 balanceOf(0x000000000000000000000000000000000000dEaD) 是否大于零。如果返回 0,说明这个保护不存在。
查金库是否追踪内部余额而非 balanceOf。 有些实现用内部记账变量(如 _accountedAssets)替代 asset.balanceOf(address(this)) 来计算 totalAssets,这样直接转账捐赠不会影响份额价格。这种设计从根源上切断了捐赠攻击的路径。你可以在合约源码或审计报告中确认 totalAssets() 的实现是否直接读取代币余额。
如果金库没有保护,你该怎么办
不要存。 尤其是金库刚部署、totalSupply 极小时。攻击者的成本就是你的存款额加 1 wei,攻击窗口在 meaningful liquidity 进入后就关闭了,但第一个存款人恰好是风险最高的。
如果一定要存,用最小金额测试并检查份额。 先存一笔你完全能承受损失的小额资产,在交易确认后检查你实际收到的份额数。如果份额数明显低于你的预期(比如按当前份额价格应该收到 100 份,实际只收到 1 份或 0 份),立即停止。但这种方法有局限:如果攻击在你测试交易和正式存款之间发生,测试通过不代表大额安全。
用支持滑点检查的前端或路由。 OpenZeppelin 的注释提到,用户可以通过一个执行检查的 wrapper(如 ERC4626Router)来保护自己,验证收到的份额数符合预期。Morpho 的文档也建议面向用户的应用实现 min-received 检查。如果你通过某个聚合器或前端存入,确认它是否在交易中设置了份额数量的下限。
一个容易被忽略的边界
份额膨胀攻击主要威胁金库的早期存款人。一旦金库积累了有意义的流动性、份额供应量足够大,同样的捐赠攻击需要投入的资产量会大到不划算,攻击窗口就关闭了。所以如果你存的是一个已经运行较久、TVL 可观的金库,这个特定风险的概率较低。但"运行较久"不等于安全:如果金库曾经被攻击过、或者份额价格因为其他原因被推到异常水平,你仍然可能收到极少的份额。存款前看一眼当前的份额价格(totalAssets / totalSupply)和过去一段时间的走势,比只看 TVL 更能说明问题。
另外,即使金库有虚拟份额保护,OpenZeppelin 也指出虚拟份额会捕获金库 accrued value 的一小部分,并且在金库亏损时,先退出的用户承担的损失会略小于后退出的用户。这是保护的代价,不是攻击,但如果你对份额价格的精确性有要求,需要知道这一点。
参考资料
- OpenZeppelin Docs·ERC-4626,2026-10-30
- OpenZeppelin Contracts v5.4.0·ERC4626.sol(源码注释),2026-10-30
- OWASP SCWE-135·ERC4626 Share Inflation via Donations,2026-10-30
- QuillAudits·Understanding ERC-4626 Vault Inflation Attacks,2026-10-30
- Zellic·Regarding inflation attacks,2026-10-30
- Morpho Docs·Vaults & ERC4626 Mechanics,2026-10-30



