通过分析代码,可以发现 delegateAndReturn 函数最终使用 delegatecall 的方式调用了 implementation 地址中的逻辑,也就是说,这是一个可升级的合约模型。而真正的 rebase 逻辑位于 YAM.sol 中, 继续跟进 rebase 函数的具体逻辑,如下: function rebase( uint256 epoch, uint256 indexDelta, bool positive ) external onlyRebaser returns (uint256) { if (indexDelta == 0) { emit Rebase(epoch, yamsScalingFactor, yamsScalingFactor); return totalSupply; } uint256 prevYamsScalingFactor = yamsScalingFactor; if (!positive) { yamsScalingFactor = yamsScalingFactor.mul(BASE.sub(indexDelta)).div(BASE); } else { uint256 newScalingFactor = yamsScalingFactor.mul(BASE.add(indexDelta)).div(BASE); if (newScalingFactor < _maxScalingFactor()) { yamsScalingFactor = newScalingFactor; } else { yamsScalingFactor = _maxScalingFactor(); } } //SlowMist// 问题代码 totalSupply = initSupply.mul(yamsScalingFactor); emit Rebase(epoch, prevYamsScalingFactor, yamsScalingFactor); return totalSupply; }}通过分析最终的 rebase 函数的逻辑,不难发现代码中根据 yamsScalingFactor 来对 totalSupply 进行调整,由于 yamsScalingFactor 是一个高精度的值,在调整完成后应当除以 BASE 来去除计算过程中的精度,获得正确的值。但是项目方在对 totalSupply 进行调整时,竟忘记了对计算结果进行调整,导致了 totalSupply 意外变大,计算出错误的结果。 分析到这里还没结束,要将漏洞和社区治理关联起来,需要对代码进行进一步的分析。通过观察 rebase 函数的修饰器,不难发现此处限定了只能是 rebaser 进行调用。而 rebaser 是 YAM 中用与实现供应量相关逻辑的合约,也就是说,是 rebaser 合约最终调用了 YAM.sol 合约中的 rebase 函数。通过跟踪相关代码,发现 rebaser 合约中对应供应量调整的逻辑为 rebase 函数,代码如下: function rebase() public { // EOA only require(msg.sender == tx.origin); // ensure rebasing at correct time _inRebaseWindow(); // This comparison also ensures there is no reentrancy. require(lastRebaseTimestampSec.add(minRebaseTimeIntervalSec) < now); // Snap the rebase time to the start of this window. lastRebaseTimestampSec = now.sub( now.mod(minRebaseTimeIntervalSec)).add(rebaseWindowOffsetSec); epoch = epoch.add(1); // get twap from uniswap v2; uint256 exchangeRate = getTWAP(); // calculates % change to supply (uint256 offPegPerc, bool positive) = computeOffPegPerc(exchangeRate); uint256 indexDelta = offPegPerc; // Apply the Dampening factor. indexDelta = indexDelta.div(rebaseLag); YAMTokenInterface yam = YAMTokenInterface(yamAddress); if (positive) { require(yam.yamsScalingFactor().mul(uint256(10**18).add(indexDelta)).div(10**18) < yam.maxScalingFactor(), "new scaling factor will be too big"); } //SlowMist// 取当前 YAM 代币的供应量 uint256 currSupply = yam.totalSupply(); uint256 mintAmount; // reduce indexDelta to account for minting //SlowMist// 计算要调整的供应量 if (positive) { uint256 mintPerc = indexDelta.mul(rebaseMintPerc).div(10**18); indexDelta = indexDelta.sub(mintPerc); mintAmount = currSupply.mul(mintPerc).div(10**18); } // rebase //SlowMist// 调用 YAM 的rebase 逻辑 uint256 supplyAfterRebase = yam.rebase(epoch, indexDelta, positive); assert(yam.yamsScalingFactor() <= yam.maxScalingFactor()); // perform actions after rebase //SlowMist// 进入调整逻辑 afterRebase(mintAmount, offPegPerc); } (责任编辑:admin) |