1. 首页
  2. 区块链资讯

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程

备受瞩目的流动性挖矿项目 YAM Finance 因合约破绽致 75 万枚 yCRV 被永远锁定,PeckShield 详解 YAM 上线的 36 小时。

原文题目:《无法复生,一最先就注定失败的 YAM 投票拯救行动!》
撰文:PeckShield

36 小时内,眼看他起高楼,几分钟内,眼看他楼塌了。

北京时间 08 月 13 日上午 03 时整,备受瞩目的 DeFi 项目 YAM Finance 宣布启动流动性挖矿,仅仅一天时间锁仓资产价值就跨越了 6 亿美元,其锁定资产增量和增速都到达了近乎癫狂状态。且照此生长,一些早期给池子注入流动性的羊毛党年利率甚至可迫近 200 倍,其疯狂水平可见一斑。

不外,正当人人都陷入挖矿狂欢的时刻,意外发生了。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程

北京时间 08 月 13 日破晓,YAM Finance 发现其智能合约的弹性供应机制(rebase)存在破绽,导致合约第二次 rebase 触发时会铸造大量分外代币,这意味着未来社区将无法获得足够的代币来执行任何治理操作,YAM 将成为一个失控的机械,最终将彻底失去社区用户的信托。

该若何拯救我们的 YAM 小红薯呢?

在发现破绽后,YAM 团队提议了「拯救行动」,称他们需要 16 万委托投票才气提交治理提案,于是向社区提议呼吁投票。很快,这场轰轰烈烈的社区投票行动就完成了。

然而,就在人人以为只是虚惊一场的时刻,北京时间 08 月 13 日下昼 16 时 01 分,YAM 创始人 Brock Elmore 却发推特称,对不起人人,我失败了。这究竟是怎么回事呢?

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程

PeckShield 平安职员介入剖析后,迅速定位到问题的本质在于:弹性供应机制(rebase)存在一个代码公式的错误,致使第二次 rebase 触发时系统会自动增发 10 ^ 18 个新代币,若是行情一直保持高位的话,那么以后的每次 rebase 触发时都市举行指数级的增发,这将使小红薯 YAM 的数目酿成一个恐怖的天文级数字。这意味着,无论后期社区怎样委托投票,都无法获得足够的投票量对系统举行控制,整个系统将陷入失控无主状态。

原本 YAM 官方招呼宽大 YAM 持有人通过署理投票的方式,一起完成此次投票「拯救行动」,以修复这个存在的破绽。然而,PeckShield 平安职员进一步剖析发现,当 YAM 官方最先发出呼吁的时刻,这次拯救行动实在就已经注定失败了。

缘故原由有二:

1)时间来不及:YAM 官方或许忽略了一点,在其提案投票拯救行动准备工作完成后,也需要至少 12.5 个小时才气被执行生效,而凭据现在的时间节奏,当其执行生效时,第二次 rebase 早已触发。

2)新部署治理合约无法被有用执行:由于第二次 rebase 触发,因此官方原先预期要执行的新治理合约到了执行时间后,却发现由于投票总量远远无法到达合约约定的总量的 4%,故而无法被有用执行。

究竟是为何呢?接下来上手艺干货:

手艺提要

首先先容下 YAM 智能合约的弹性供应机制(rebase):

1)系统会凭据市场价钱浮动来动态调整代币的供应量,当市价上涨时则按比例增发代币,以降低单元代币的价值,直至降至 1 美元。

2)天天划分执行两次 rebase,每次 rebase 会改变代币供应量,凭据市场现价增发或销毁一定量的代币。

再说一个实行提案的要害因素:持有者举行委托投票,投票数跨越总量的 1%,则提案才可以举行执行排列,且按合约约定执行排列时长需要守候 12.5 小时,而提案执行时,则投票需要跨越总量的 4%。云云新治理合约才气执行生效,项目才气继续正常运转。

有了以上几个手艺要点的铺垫,我们再来看一下,YAM 官方的跟进时间表,就能明了此次拯救行动为什么注定会失败。

如下图时间线所示:

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程

②是第一次 rebase 触发的时间,由于合约的 bug 导致 totalsupply 资产发生异常暴涨,官方发现 BUG 存在并举行了披露。

③是官方宣布提议部署新治理合约的时间,在此之后社区最先启动投票。

④是投票目的开端完成,新治理合约进入执行排列的时间,自此守候执行 12.5 小时合约正式执行。

⑤是第二次 rebase 的触发时间。

⑦是其新治理合约投票通事后正式执行的时间。

⑥ 在第二次 rebase 触发后的第 31 分钟时,或许是项目方发现了已经无力回天了,提案作废乐成,项目方正式宣布 YAM 失败。

①之后的绿色区域是投票和提案拯救行动可以乐成的「黄金抢救期」,需要整个拯救行动准备工作在第一次 rebase 触发之前半小时内完成。(即蓝色虚线应提前到绿色区域内)。

Yam Finance 计划在未来几周启动代币迁移计划

链闻消息, DeFi 项目 Yam Finance 启动迁移计划,计划让社区控制项目。如果获得社区支持,将未来几周通过两个阶段的启动程序推动 YAM 协议迁移到审计后的合约地址。 Yam Finance 表示,在任何时候都没有也不打算对协议进行管理和控制。在成功迁移之后,计划奖励…DeFi,Yam Finance,YAM,YAMv3,代币迁移,YAMv2

这意味着,YAM 官方应在第一次 rebase (北京时间 08 月 13 日破晓 04:08) 之前就应该发现这个破绽,而且留有足够的时间完成新治理合约部署和投票。

可是事与愿违,官方发现破绽并披露呼吁投票的时间照样太晚了,错过了唯一能够乐成的黄金抢救期。而更糟糕的是,凭据官方的时间节奏,当新治理合约到了⑦执行的时间后,投票数要跨越总量的 4% 才行,而现在的总量已经扩大了 10^18*10^18,此前累积的投票数已然杯水车薪,基本无济于事。

以是,这次拯救行动一最先就注定了会失败。

下面我们会对此次事宜做下详细剖析:(项目方 github 地址

详细历程剖析

首先我们看下当第一次 rebase 发生了什么:

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 1 第一次 rebase 资产转变

如上面 链上信息 所示,当第一次 rebase 之后,totalSupply 从 3,500,000* 10^18 暴涨到一个极大值。

我们进一步剖析代码,看下在代码中发生了什么:首先从链上信息我们能看到 rebase 操作挪用的是 YAMRebaser 合约的 YAMRebaser::rebase() 函数(我们先跳过这个函数稍后再讲),我们最终发现它通过挪用 YAM 合约(0xa923af6d05993495257a872ec69dbbf01501eb0e)的 rebase() 函数重新盘算 totalSupply (代码逻辑如下图 2 中所示),在第 340 行的 totalSupply 赋值操作可以看到,这一行代码有个显著的错误——没有除 BASE,从而导致 totalSupply 的值暴增了 10^18 倍。

YAM 官方在第一次 rebase 以后发现了这个问题,于是披露 rebase bug 事宜启动了投票拯救行动。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 2 YAMToken::rebase() 获得一个异常大的 totalSupply 值

而在 12 小时之后,YAM 又触发了第二次 rebase ,这个数据又是以基于错误的 totalSupply 来盘算的,从而导致 initSupply 的数值同样泛起了异常。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 3 第二次 rebase 资产转变

我们继续剖析造成 initSupply 异常的成因,要害在上面提到过 YAMRebaser::rebase() 函数,这个函数实现的主要逻辑:先基于 yam.totalSupply() 盘算出本次 rebase 需要增发的 YAM 数额 mintAmount,在 afterRebase() 函数经过数层挪用后进入 YAM 的_mint() 函数,基于异常的 mintAmount 给 initSupply 举行赋值。由于在第一次 rebase 中,totalySupply 已经酿成一个极大值,以是基于此异常值的后续一列操作(如图 4 中红色箭头所示)最终导致 initSupply 也盘算错误,酿成了一个天文级的数值。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 4 YAMRebaser::rebase() 用错误的 totalSupply 盘算 initSupply

当第一次 rebase 泛起异常时,项目方已经发现问题并决议提出一个修复系统的提案(proposal),希望通过投票的方式将此提案排入执行行列而且执行。当此题案收到足够多的投票,治理合约(Governor)允许任何人通过挪用 GovernorAlpha::queue() 函数将此题案排入执行行列。但由于此治理合约代码逻辑的实现,导致无论是在第二次 rebase 之 前或是之后举行修复,都无法准确执行这个拯救行动。

为什么说项目方准备工作完成的太晚了?

我们看下图中的 GovernorAlpha::queue() 代码,我们注重到了在挪用_queueOrRevert 函数之前的第 224 行中设置变量 eta = current timestamp + timelock.delay (12.5 小时),这就使得生效时间一定在加入行列的 12.5 小时以后,而第二个 rebase 时间是与第一次距离 12 小时,这就意味着 要执行乐成需要将拯救行动提前到第一次 rebase 之前至少半小时以上,否则将永远无法执行。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 5 GovernorAlpha::queue() 函数设置 eta (生效时间)

又为什么说已经做出的拯救行动,基本无济于事呢?

当触发合约 GovernorAlpha::execute() 时首先会先执行 state 函数来获得当前提案状态。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 6 GovernorAlpha::execute() 检测提案状态

在下面的 state() 函数 第 330 行,若是 proposal.forVotes <= againstVotes() ,提案状态被设置为失败。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 7 GovernorAlpha::state() 执行返回 Defeated 错误

从代码中能看出来,项目方在设计系统时,投票数被设计为必须大于 initSupply 总量的 4%,此提案才气是正当的状态,如下图所示。然而,当第二次执行 rebase 以后,initSupply 已经被搞成一个极大值。这就导致了,投票票数(forVotes)永远不可能 >= quorumVotes(),从而总是返回 Defeated。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 8 GovernorAlpha::quorumVotes() 返回一个错误的异常值

除了提案状态异常的问题之外,如图 9、图 10 所示,当第二次 rebase 发生以后,由于 GovernorAlpha :: propose() 检查投票数一定小于 proposalThreshold (1% 的 initSupply),因此新的提案也再也无法被提出,更遑论要投票执行了。

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 9 GovernorAlpha::propose() 检测投票数是否大于 1% initSupply

PeckShield:技术拆解 YAM 自救失败 ,75 万 yCRV 被锁全过程图 10 GovernorAlpha::proposalThreshold() 返回 1% initSupply

总结

此次 YAM 破绽事宜,最终造成治理合约中 75 万枚 yCRV 被永远锁定,而且短时间内的急速暴跌和无力回天的局势,不知道有多少人被埋在了价钱高点,其疯狂水平成了现在 DeFi 流动性挖矿的最真实写照,其残酷魔幻水平何尝又不是?倘若项目方在部署合约之前但凡测试过一次 rebase 流程,必定能捕捉到破绽的存在。足以见得,DeFi 项目做平安审计的重要性。

综上剖析,PeckShield (派盾) 想借此劝诫诸君,在区块链天下里,务必要对每一行合约代码保持敬畏,由于任何细微的疏漏都可能造成无法挽回的局势。究竟,代码是人写的,破绽也很难被彻底制止,因此需要项目方在合约部署上线前就做好充实的测试和第三方平安审计工作,这会辅助其更早发现并排查合约代码潜在的平安破绽,不至于等到,破绽发生后,亡羊补牢,为时已晚。

泉源链接:mp.weixin.qq.com

原创文章,作者:链大大,如若转载,请注明出处:http://www.chaindada.com/chain/14940.html