Summary
Staking.updateFor() calculates claimable amount based on _supplied * delta / 1e18.
It might cause precision loss but supplyIndex is updated.
Vulnerability Details
Let's say _supplied * _delta is 0.999e18, newly claimable shares will be zero, but supplyIndex will be updated to current index. So there's no way to recover lost precision.
function updateFor(address recipient) public {
update();
uint256 _supplied = balances[recipient];
if (_supplied > 0) {
uint256 _supplyIndex = supplyIndex[recipient];
supplyIndex[recipient] = index;
uint256 _delta = index - _supplyIndex;
if (_delta > 0) {
uint256 _share = _supplied * _delta / 1e18;
claimable[recipient] += _share;
}
} else {
supplyIndex[recipient] = index;
}
}
Impact
If staking amount is small or updateFor is called frequently, user may lose staking reward because of precision loss.
Tools Used
Manual Review
Recommendations
It should divide by 1e18 in claim() not updateFor.
function claim() external {
updateFor(msg.sender);
- WETH.transfer(msg.sender, claimable[msg.sender]);
+ WETH.transfer(msg.sender, claimable[msg.sender] / 1e18);
claimable[msg.sender] = 0;
balance = WETH.balanceOf(address(this));
}
function updateFor(address recipient) public {
update();
uint256 _supplied = balances[recipient];
if (_supplied > 0) {
uint256 _supplyIndex = supplyIndex[recipient];
supplyIndex[recipient] = index;
uint256 _delta = index - _supplyIndex;
if (_delta > 0) {
- uint256 _share = _supplied * _delta / 1e18;
+ uint256 _share = _supplied * _delta;
claimable[recipient] += _share;
}
} else {
supplyIndex[recipient] = index;
}
}