pragma solidity ^0.8.0;
contract Calculator {
functioncalculate(uint256 a, uint256 b) publicpurereturns (uint256) {
require(b != 0, "Denominator cannot be zero");
return a / b;
}
}
我输入了9999999和10000000,可看到,因为分子小于分母,所以直接向下取整为0。
如果是10/4=2.5,也会被向下取整为2
说明完合约进行计算的特征后,进入借贷池合约中来进行模拟漏洞:https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Precision-loss.sol
如下合约展示了一个简单的借贷池合约SimplePool,部署合约时即开始计算利息_reward,但是以下代码存在利息计算公式 (totalDebt * _timeDelta) / (31536000 * 1e18) 中的除法可能会导致精度损失。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
//一个借贷池合约
contract SimplePool {
uintpublic totalDebt; //代表债务
uintpublic lastAccrueInterestTime; //生成时间
uintpublic loanTokenBalance; //借贷池中的代币余额
constructor() {
totalDebt = 10000e6; //借了10000000000个代币
lastAccrueInterestTime = block.timestamp - 1; //记录现在的时间
loanTokenBalance = 500e18; //借贷池里有500W亿代币
}
//合约
function getCurrentReward() public view returns (uint _reward) {
// 获取自上次计息以来经过的时间
uint _timeDelta = block.timestamp - lastAccrueInterestTime; //
// 如果时间差为0 则返回0
if (_timeDelta == 0) return0;
// 计算利息,随着时间不断增加
//10000000000 * 1 / (365 days * 1000000000000000000
_reward = (totalDebt * _timeDelta) / (31536000 * 1e18);
_reward;
}
}
主要的计算利息算法为
_reward = (totalDebt * _timeDelta) / (365 days * 1e18);
1e18
调整精度。_reward(利息)正常来说会越来越大。所以计算利息_reward的方式是随着时间(每秒不断增加的),但是一眼可以看出分子在前期是明显小于分母的,所以利息为0会持续非常久的一段时间。
利息存在很久时间为0就是漏洞问题所在。
用户可以进行借款,但是在短时间内还款是无需利息的,可是实际上我们代码原本的逻辑就是从借钱的一瞬间就开始产生利息。
上面的接待池测试代码利息年利率为100%,也就是借100块钱,在1年后需要还款200块。
实际上这段代码的从业务逻辑角度来说,也不能说是存在漏洞,只能说是存在非预期的可能损失部分利息的bug。就算是给客户几天的时间免息,如果是预期的行为也是非常正常的。
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论