Some rewards might be locked inside the contract due to the rounding loss.
_claimAndSyncRewards() claimed the rewards from the staking contract and tracks rewardsPerShare with the current supply.
function _claimAndSyncRewards() internal virtual {
uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
return;
}
_claimAssetRewards();
uint256 balanceAfterClaimingRewards = rewardToken.balanceOf(address(this));
uint256 _rewardsPerShare = rewardsPerShare;
uint256 _previousBalance = lastRewardBalance;
if (balanceAfterClaimingRewards > _previousBalance) {
uint256 delta = balanceAfterClaimingRewards - _previousBalance;
// {qRewards/share} += {qRewards} * {qShare/share} / {qShare}
_rewardsPerShare += (delta * one) / _totalSupply; //@audit possible rounding loss
}
lastRewardBalance = balanceAfterClaimingRewards;
rewardsPerShare = _rewardsPerShare;
}
It uses one as a multiplier and from this setting we know it has the same decimals as underlying(thus totalSupply).
My concern is _claimAndSyncRewards() is called for each deposit/transfer/withdraw in _beforeTokenTransfer() and it will make the rounding problem more serious.
Manual Review
I think there would be 2 mitigation.
Use a bigger multiplier.
Keep the remainders and use them next time in _claimAndSyncRewards() like this.
if (balanceAfterClaimingRewards > _previousBalance) {
uint256 delta = balanceAfterClaimingRewards - _previousBalance; //new rewards
uint256 deltaPerShare = (delta * one) / _totalSupply; //new delta per share
// decrease balanceAfterClaimingRewards so remainders can be used next time
balanceAfterClaimingRewards = _previousBalance + deltaPerShare * _totalSupply / one;
_rewardsPerShare += deltaPerShare;
}
lastRewardBalance = balanceAfterClaimingRewards;
Math
The text was updated successfully, but these errors were encountered:
All reactions