Platform: Code4rena
Start Date: 07/03/2024
Pot Size: $63,000 USDC
Total HM: 20
Participants: 36
Period: 5 days
Judge: cccz
Total Solo HM: 11
Id: 349
League: BLAST
Rank: 21/36
Findings: 1
Award: $208.83
🌟 Selected for report: 0
🚀 Solo Findings: 0
208.8293 USDC - $208.83
LockingMultiRewards.sol, Users can get rewards less than expected amount because of rounding error on calculation of rewardRate. If rewardToken is token of low decimals, like GUSD, which is stablecoin token with 2 decimals.
In LockingMultiRewards.sol::notifyRewardAmount, rewardRate is calculated as following.
reward.rewardRate = amount / _remainingRewardTime;
The maximum of _remainingRewardTime is 7 * 24 * 3600 = 604_800
, so if operator tries to notify GUSD reward amount less than 604800 ($6048) at the start of epoch, it always reverts with the line (https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L384)
Even though reward amount is greater than 604800($6048), there will be precision issue, that is, real rewarded amount is less than notified amount.
POC example:
let's say that only a user staked and all rewards will be awarded to the user. reward amount: 1_000_000 remainingRewardTime: 604_800 reward period: 604_800 rewardRate = 1_000_000 / 604_800 = 1 real reward = rewardRate * 604_800 = 604_800 diff = 1_000_000 - 604_800 = 395200 ($3952)
It will affect the protocol's reputation, so we need to add precision value
Manual Review
We need to add precision value.
function notifyRewardAmount(address rewardToken, uint256 amount, uint minRemainingTime) public onlyOperators { ...... - amount += _remainingRewardTime * reward.rewardRate; + amount += _remainingRewardTime * reward.rewardRate / 10 ** 18; ...... - reward.rewardRate = amount / _remainingRewardTime; + reward.rewardRate = amount * 10 ** 18 / _remainingRewardTime; ...... } function rewardsForDuration(address rewardToken) external view returns (uint256) { - return _rewardData[rewardToken].rewardRate * rewardsDuration; + return _rewardData[rewardToken].rewardRate * rewardsDuration / 10 ** 18; } function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) public view returns (uint256) { ...... - uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate * 1e18) / totalSupply_; + uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate) / totalSupply_; ...... }
Math
#0 - c4-pre-sort
2024-03-15T12:55:17Z
141345 marked the issue as duplicate of #166
#1 - 141345
2024-03-15T12:55:59Z
low decimal token results in small amount
#2 - c4-judge
2024-03-29T13:30:35Z
thereksfour changed the severity to QA (Quality Assurance)
#3 - c4-judge
2024-03-29T16:52:20Z
thereksfour marked the issue as grade-c
#4 - c4-judge
2024-04-07T08:52:26Z
This previously downgraded issue has been upgraded by thereksfour
#5 - c4-judge
2024-04-07T08:56:29Z
thereksfour marked the issue as satisfactory