Summary
_rewardPerToken()
output value has been capped at uint256 which wrongfully converted into uin216 in updateReward
modifier
Vulnerability Details
_rewardPerToken has a return value of data type uint256 as you can see here
function _rewardPerToken() internal view returns (uint256) {
if (totalSupply == 0) {
return rewardData.rewardPerTokenStored;
}
return
rewardData.rewardPerTokenStored +
(((_lastTimeRewardApplicable(rewardData.periodFinish) -
rewardData.lastUpdateTime) *
rewardData.rewardRate * 1e18)
/ totalSupply);
}
Issue arises when this function gets internally called whenever updateReward modifier is being used as in this modifier _rewardPerToken
gets wrongfully casted into uint216
.
modifier updateReward(address _account, uint256 _index) {
{
rewardData.rewardPerTokenStored = uint216(_rewardPerToken());
rewardData.lastUpdateTime = uint40(_lastTimeRewardApplicable(rewardData.periodFinish));
if (_account != address(0)) {
StakeInfo memory _stakeInfo = _stakeInfos[_account][_index];
uint256 vestingRate = _getVestingRate(_stakeInfo);
claimableRewards[_account][_index] = _earned(_stakeInfo, _account, _index);
userRewardPerTokenPaid[_account][_index] = vestingRate * uint256(rewardData.rewardPerTokenStored) / 1e18;
}
}
_;
}
Due to this precision loss happens leading to wrong rewardPerToken which leads to wrong balance updates.
Impact
Loss of precision in _rewardPerToken
This leads to wrong balance updates in the updateReward
modifier.
Tools Used
Manual review
Recommendations
modifier updateReward(address _account, uint256 _index) {
{
// stack too deep
- rewardData.rewardPerTokenStored = uint216(_rewardPerToken());
+ rewardData.rewardPerTokenStored = uint256(_rewardPerToken());
rewardData.lastUpdateTime = uint40(_lastTimeRewardApplicable(rewardData.periodFinish));
if (_account != address(0)) {
StakeInfo memory _stakeInfo = _stakeInfos[_account][_index];
uint256 vestingRate = _getVestingRate(_stakeInfo);
claimableRewards[_account][_index] = _earned(_stakeInfo, _account, _index);
userRewardPerTokenPaid[_account][_index] = vestingRate * uint256(rewardData.rewardPerTokenStored) / 1e18;
}
}
_;
}