技能提要
北京时间08月13日破晓,YAM Finance发明其智能合约的弹性供给机制(rebase)存在裂痕,导致合约第二次 rebase 触发时会锻造大量特别代币,这意味着将来社区将无法得到足够的代币来执行任何管理操纵,YAM 将成为一个失控的呆板,最终将彻底失去社区用户的信任。
图9. GovernorAlpha::propose()?检测投票数是否大于1% initSupply
③是官方公布提议陈设新管理合约的时间,在此之后社区开始启动投票。
④是投票方针劈头完成,新管理合约进入执行分列的时间,自此期待执行12.5小时合约正式执行。
此次?YAM 裂痕事件,最终造成管理合约中75万枚?yCRV?被永久锁定,并且短时间内的急速暴跌和无力回天的排场,不知道有几多人被埋在了价值高点,其猖獗水平成了如今 DeFi 活动性挖矿的最真实写照,其残忍魔幻水平何尝又不是?倘若项目方在陈设合约之前但凡测试过一次 rebase 流程,肯定能捕获到裂痕的存在。足以见得,DeFi 项目做安详审计的重要性。
①之后的绿色区域是投票和提案拯救动作可以乐成的“黄金抢救期”,需要整个拯救动作筹备事情在第一次 rebase 触发之前半小时内完成。(即蓝色虚线应提前到绿色区域内)。
下面我们会对此次事件做下具体阐明:(项目方github地点 https://github.com/yam-finance/yam-protocol)
图6. GovernorAlpha::execute()? 检测提案状态
图10. GovernorAlpha::proposalThreshold()? 返回1% initSupply
不外,合法各人都陷入挖矿狂欢的时候,意外产生了。
该如何拯救我们的 YAM 小红薯呢?
在发明裂痕后,YAM 团队提倡了“拯救动作”,称他们需要16万委托投票才气提交管理提案,,于是向社区提倡号令投票。很快,这场大张旗鼓的社区投票动作就完成了。
2)新陈设管理合约无法被有效执行:由于第二次 rebase 触发,因此官方原先预期要执行的新管理合约到了执行时间后,却发明由于投票总量远远无法到达合约约定的总量的4%,故而无法被有效执行。
具体进程阐明
如下图时间线所示:
1)时间来不及:?YAM 官方或者忽略了一点,在其提案投票拯救动作筹备事情完成后,也需要至少12.5个小时才气被执行生效,而凭据此刻的时间节拍,当其执行生效时,第二次 rebase 早已触发。
总结
图3. 第二次 rebase 资产变革
如上面链上信息(https://oko.palkeo.com/0x7b9017ec92b0200455e5269380195fbecfbf91c8acda30985cc1dc413d215076/)所示,当第一次 rebase ?之后,totalSupply从3,500,000* 10^18暴涨到一个极大值。
为什么说项目方筹备事情完成的太晚了?
⑤是第二次 rebase 的触发时间。
北京时间08月13日上午03时整,备受瞩目标 DeFi 项目 YAM Finance 公布启动活动性挖矿,仅仅一天时间锁仓资产代价就高出了6亿美元,其锁定资产增量和增速都到达了近乎癫狂状态。且照此成长,一些早期给池子注入活动性的羊毛党年利率甚至可迫近200倍,其猖獗水平可见一斑。
原来 YAM 官方招呼宽大 YAM 持有人通过署理投票的方法,一起完成此次投票“拯救动作”,以修复这个存在的裂痕。然而,PeckShield 安详人员进一步阐明发明,当YAM 官方开始发出号令的时候,这次拯救动作其实就已经注定失败了。
图8. GovernorAlpha::quorumVotes()?返回一个错误的异常值
我们进一步阐明代码,看下在代码中产生了什么:首先从链上信息我们能看到 rebase 操纵挪用的是 YAMRebaser 合约的 YAMRebaser::rebase()?函数(我们先跳过这个函数稍后再讲),我们最终发明它通过挪用 YAM合约(0xa923af6d05993495257a872ec69dbbf01501eb0e)的 rebase()?函数从头计较totalSupply(代码逻辑如下图2中所示),在第340行的 totalSupply 赋值操纵可以看到,这一行代码有个明明的错误——没有除 BASE,从而导致 totalSupply 的值暴增了10^18倍。
从代码中能看出来,项目方在设计系统时,投票数被设计为必需大于 initSupply 总量的4%,此提案才气是正当的状态,如下图所示。然而,当第二次执行 rebase 今后,initSupply 已经被搞成一个极大值。这就导致了,投票票数(forVotes)永远不行能 >= quorumVotes(),从而老是返回 Defeated。
首先先容下 YAM 智能合约的弹性供给机制(rebase):
当触发合约 GovernorAlpha::execute()?时首先会先执行 state 函数来获恰当前提案状态。
我们继承阐明造成 initSupply 异常的成因,要害在上面提到过 YAMRebaser::rebase()?函数,这个函数实现的主要逻辑:先基于 yam.totalSupply() 计较出本次 rebase 需要增发的 YAM 数额 mintAmount,在 afterRebase()?函数颠末数层挪用后进入 YAM 的?_mint()?函数,基于异常的 mintAmount 给 initSupply 举办赋值。由于在第一次 rebase 中,totalySupply 已经酿成一个极大值,所以基于此异常值的后续一列操纵(如图4中赤色箭头所示)最终导致 initSupply 也计较错误,酿成了一个天文级的数值。
②是第一次 rebase 触发的时间,由于合约的 bug 导致 totalsupply 资产产生异常暴涨,官方发明 BUG 存在并举办了披露。
在下面的 state()?函数 第330行,假如 proposal.forVotes <= againstVotes() ,提案状态被配置为失败。
综上阐明,PeckShield(派盾)?想借此劝诫诸君,在区块链世界里,务须要对每一行合约代码保持敬畏,因为任何细微的疏漏都大概造成无法挽回的排场。究竟,代码是人写的,裂痕也很难被彻底制止,因此需要项目方在合约陈设上线前就做好充实的测试和第三方安详审计事情,这会辅佐其更早发明并排查合约代码潜在的安详裂痕,不至于比及,裂痕产生后,亡羊补牢,为时已晚
有了以上几个技能要点的铺垫,我们再来看一下,YAM 官方的跟进时间表,就能大白此次拯救动作为什么注定会失败。
首先我们看下当第一次 rebase 产生了什么:
图7. GovernorAlpha::state()?执行返回Defeated错误
图5. GovernorAlpha::queue()? 函数配置eta(生效时间)
36小时内,眼看他起高楼,几分钟内,眼看他楼塌了。
而在12小时之后,YAM 又触发了第二次 rebase(https://oko.palkeo.com/0x32735e9e9aac51739b5725a225be6c7a3851f422be986d0f4f4bc0ec475ee286/),这个数据又是以基于错误的 totalSupply 来计较的,从而导致 initSupply 的数值同样呈现了异常。
原因有二:
我们看下图中的 GovernorAlpha::queue() 代码,我们留意到了在挪用 _queueOrRevert 函数之前的第224行中配置变量 eta = current timestamp + timelock.delay(12.5 小时),这就使得生效时间一定在插手行列的12.5小时今后,而第二个 rebase 时间是与第一次隔断12小时,这就意味着要执行乐成需要将拯救动作提前到第一次 rebase 之前至少半小时以上,不然将永远无法执行。
PeckShield 安详人员参与阐明后,迅速定位到问题的本质在于:弹性供给机制(rebase)存在一个代码公式的错误,致使第二次 rebase 触发时系统会自动增发10 ^ 18个新代币,假如行情一直保持高位的话,那么今后的每次 rebase 触发时城市举办指数级的增发,这将使小红薯 YAM 的数量酿成一个可骇的天文级数字。这意味着,无论后期社区奈何委托投票,都无法得到足够的投票量对系统举办节制,整个系统将陷入失控无主状态。
毕竟是为何呢?接下来上技醒目货:
当第一次 rebase 呈现异常时,项目方已经发明问题并抉择提出一个修复系统的提案(proposal),但愿通过投票的方法将此提案排入执行行列而且执行。当此题案收到足够多的投票,管理合约(Governor)答允任何人通过挪用 GovernorAlpha::queue() 函数将此题案排入执行行列。但由于此管理合约代码逻辑的实现,导致无论是在第二次 rebase之?前或是之后举办修复,都无法正确执行这个拯救动作。
图1. 第一次 rebase 资产变革
图2. YAMToken::rebase() 获得一个异常大的totalSupply值
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。