慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?

原文标题:《DeFi YAM,一行代码如何蒸发数亿美元?》
撰文:yudan @ 慢雾安全团队
,据链闻消息, 2020 年 8 月 13 日,知名以太坊 DeFi 项目 YAM 官方通过 Twitter 发文表明发现合约中存在漏洞,24 小时内价格暴跌 99% 。慢雾安全团队在收到情报后快速进行了相关的跟进及分析,以下是详细的技术细节。,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,以上是 YAM 官方对本次事件的
简短说明。,简单来说就是官方在合约中发现负责调整供应量的函数发生了问题,这个问题导致多余的 YAM 代币放进了 YAM 的 reserves 合约中,并且如果不修正这个问题,将会导致 YAM 的后续治理变为不可能。同时,官方给出了此次漏洞的具体问题代码,如下:,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,从上图可知,由于编码不规范,YAM 合约在调整 totalSupply 的时候,本应将最后的结果除以 BASE 变量,但是在实际开发过程中却忽略了,导致 totoalSupply 计算不正确,比原来的值要大 10^18 倍。但是代币供应量问题和治理是怎么扯上关系呢?这需要我们针对代码做进一步的分析。,为了深入了解此次漏洞造成的影响,需要对 YAM 项目代码进行深入的了解。根据官方给出的问题代码及项目 Github 地址(
https://github.com/yam-finance/yam-protocol),可以定位出调整供应量的
rebase 函数位于 YAMDelegator.sol 合约中,具体代码如下:,通过跟踪
rebase 函数,发现
rebase 函数最终调用了 delegateAndReturn 函数,代码如下:,通过分析代码,可以发现 delegateAndReturn 函数最终使用 delegatecall 的方式调用了 implementation 地址中的逻辑,也就是说,这是一个可升级的合约模型。而真正的
rebase 逻辑位于 YAM.sol 中 , 继续跟进 rebase 函数的具体逻辑,如下:,通过分析最终的
rebase 函数的逻辑,不难发现代码中根据 yamsScalingFactor 来对 totalSupply 进行调整,由于 yamsScalingFactor 是一个高精度的值,在调整完成后应当除以 BASE 来去除计算过程中的精度,获得正确的值。但是项目方在对 totalSupply 进行调整时,竟忘记了对计算结果进行调整,导致了 totalSupply 意外变大,计算出错误的结果。,分析到这里还没结束,要将漏洞和社区治理关联起来,需要对代码进行进一步的分析。通过观察
rebase 函数的修饰器,不难发现此处限定了只能是 rebaser 进行调用。而 rebaser 是 YAM 中用与实现供应量相关逻辑的合约,也就是说,是 rebaser 合约最终调用了 YAM.sol 合约中的
rebase 函数。通过跟踪相关代码,发现 rebaser 合约中对应供应量调整的逻辑为
rebase 函数,代码如下:,通过分析代码,可以发现函数在进行了一系列的检查后,首先获取了当前 YAM 的供应量,计算此次的铸币数量,然后再调用 YAM.sol 中的
rebase 函数对 totalSupply 进行调整,也就是说 rebase 过后的对 totalSupply 的影响要在下一次调用 rebaser 合约的
rebase 函数才会生效。最后
rebase 函数调用了 afterRebase 函数。我们继续跟进 afterRebase 函数中的代码:,通过分析发现, afterRebase 函数主要的逻辑在 buyReserveAndTransfer 函数中,此函数用于将增发出来的代币的一部分用于到 Uniswap 中购买 yCRV 代币。跟踪 buyReserveAndTransfer 函数,代码如下:,通过对代码分析,buyReserveAndTransfer 首先会计算在 Uniswap 中用于兑换 yCRV 的 YAM 的数量,如果该数量少于 YAM 的铸币数量,则会将多余的增发的 YAM 币给 reserves 合约,这一步是通过 Uniswap 合约调用
rebase 合约的 uniswapV2Call 函数实现的,具体的代码如下:,分析到这里,一个完整的
rebase 流程就完成了,你可能看得很懵,我们用简单的流程图简化下:,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,也就是说,每次的
rebase,如果有多余的 YAM 代币,这些代币将会流到 reserves 合约中,那这和社区治理的关系是什么呢?,通过分析项目代码,发现治理相关的逻辑在 YAMGovernorAlpha.sol 中,其中发起提案的函数为 propose,具体代码如下:,通过分析代码,可以发现在发起提案时,需要提案发起人拥有一定额度的票权利,这个值必须大于 proposalThreshold 计算得来的值,具体代码如下:,也就是说提案发起人的票权必须大于 initSupply 的 1% 才能发起提案。那 initSupply 受什么影响呢?答案是 YAM 代币的 mint 函数,代码如下:,从代码可知,mint 函数在每次铸币时都会更新 initSupply 的值,而这个值是根据 amount 的值来计算的,也就是铸币的数量。,现在,我们已经分析完所有的流程了,剩下的就是把所有的分析串起来,看看这次的漏洞对 YAM 产生了什么影响,对上文的流程图做拓展,变成下面这样:,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,整个事件的分析如上图,由于
rebase 的时候取的是上一次的 totalSupply 的值,所以计算错误的 totalSupply 的值并不会立即通过 mint 作用到 initSupply 上,所以在下一次
rebase 前,社区仍有机会挽回这个错误,减少损失。但是一旦下一次
rebase 执行,整个失误将会变得无法挽回。,通过查询 Etherscan 上 YAM 代币合约的相关信息,可以看到 totalSupply 已经到了一个非常大的值,而 initSupply 还未受到影响。,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,慢雾:DeFi 当红项目 YAM 闪电折戟,一行代码如何蒸发数亿美元?,这次事件中官方已经给出了具体的修复方案,这里不再赘述。这次的事件充分暴露了未经审计 DeFi 合约中隐藏的巨大风险,虽然 YAM 开发者已经在 Github 中表明 YAM 合约的很多代码是参考了经过充分审计的 DeFi 项目如 Compound、Ampleforth、Synthetix 及 YEarn/YFI,但是仍无可避免地发生了意料之外的风险。,DeFi 项目 Yam Finance (YAM) 核心开发者 belmore 在推特上表示:「对不起,大家。我失败了。谢谢你们今天的大力支持。我太难过了。」但是覆水已经难收,在此,慢雾安全团队给出如下
建议:,1、由于 DeFi 合约的高度复杂性,任何 DeFi 项目都需在经过专业的安全团队充分审计后再进行上线,降低合约发生意外的风险 。
审计可联系慢雾安全团队(team@slowmist.com,2、项目中去中心化治理应循序渐进,在项目开始阶段,需要设置适当的权限以防发生黑天鹅事件。,通过分析发现, afterRebase 函数主要的逻辑在 buyReserveAndTransfer 函数中,此函数用于将增发出来的代币的一部分用于到 Uniswap 中购买 yCRV 代币。跟踪 buyReserveAndTransfer 函数,代码如下:,通过对代码分析,buyReserveAndTransfer 首先会计算在 Uniswap 中用于兑换 yCRV 的 YAM 的数量,如果该数量少于 YAM 的铸币数量,则会将多余的增发的 YAM 币给 reserves 合约,这一步是通过 Uniswap 合约调用
rebase 合约的 uniswapV2Call 函数实现的,具体的代码如下:,也就是说提案发起人的票权必须大于 initSupply 的 1% 才能发起提案。那 initSupply 受什么影响呢?答案是 YAM 代币的 mint 函数,代码如下:

欢迎加入社群 探讨web3,分享项目空投信息  https://discord.gg/RW94PbPv3p

原创文章,作者:admin,如若转载,请注明出处:http://www.lianchaguan.com/archives/20111

(0)
上一篇 2020年12月21日 下午3:50
下一篇 2020年12月21日 下午3:51

相关推荐