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: 19/36
Findings: 2
Award: $224.16
π Selected for report: 0
π Solo Findings: 0
208.8293 USDC - $208.83
notifyRewardAmount
FunctionThe existing notifyRewardAmount
function is susceptible to precision loss, even when the condition if (amount < _remainingRewardTime)
is satisfied. This vulnerability becomes apparent even in scenarios where the amount is substantial (e.g., 172799). The current unscaled equation (reward.rewardRate = amount / _remainingRewardTime;
) results in precision loss, leading to inaccurate reward rates.
amount < _remainingRewardTime
is true, the function proceeds without reverting.reward.rewardRate = amount / _remainingRewardTime;
results in a calculated reward rate of 1.9999884259259259, which Solidity truncates to 1, leading to a precision loss of 0.9999 plus.Manual code analysis.
Since Rewardrate has already been used in some functions consider scaling down in the two functions affected. PRECISION_FACTOR can be set to 1e18 as used in calculating rewardpertokenstored. To address the precision loss issue, it is essential to scale the equation appropriately. Update the code as follows:
// Scale the equation to prevent precision loss reward.rewardRate = amount * PRECISION_FACTOR / _remainingRewardTime;
function rewardsForDuration(address rewardToken) external view returns (uint256) { return _rewardData[rewardToken].rewardRate * rewardsDuration/ PRECISION_FACTOR ;``` 3.Remove scale because it has already been used while calculating rewardrate ```solidity function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) public view returns (uint256) { if (totalSupply_ == 0) { return _rewardData[rewardToken].rewardPerTokenStored; } uint256 timeElapsed = lastTimeRewardApplicable_ - _rewardData[rewardToken].lastUpdateTime; -- uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate * 1e18) / totalSupply_; ++ uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate) / totalSupply_; return _rewardData[rewardToken].rewardPerTokenStored + pendingRewardsPerToken; } ``` 4. earn function has already been scaled down so it can retain it's details ```solidity function _earned(address user, uint256 balance_, address rewardToken, uint256 rewardPerToken_) internal view returns (uint256) { uint256 pendingUserRewardsPerToken = rewardPerToken_ - userRewardPerTokenPaid[user][rewardToken]; return ((balance_ * pendingUserRewardsPerToken) / 1e18) + rewards[user][rewardToken]; } Ensure to define `PRECISION_FACTOR` appropriately in your contract. This modification guarantees accurate reward rate calculations and eliminates precision loss concerns.``` By incorporating this adjustment, the `notifyRewardAmount` function will maintain precision, ensuring reliable and accurate reward rates. ## Assessed type Decimal
#0 - c4-pre-sort
2024-03-15T12:54:53Z
141345 marked the issue as duplicate of #171
#1 - c4-pre-sort
2024-03-15T12:55:02Z
141345 marked the issue as not a duplicate
#2 - c4-pre-sort
2024-03-15T12:55:08Z
141345 marked the issue as duplicate of #166
#3 - c4-pre-sort
2024-03-15T12:55:11Z
141345 marked the issue as duplicate of #166
#4 - c4-judge
2024-03-29T13:30:35Z
thereksfour changed the severity to QA (Quality Assurance)
#5 - c4-judge
2024-04-07T08:52:26Z
This previously downgraded issue has been upgraded by thereksfour
#6 - c4-judge
2024-04-07T08:54:54Z
thereksfour marked the issue as satisfactory
π Selected for report: ether_sky
Also found by: 0x11singh99, 0xE1, 0xJaeger, Bauchibred, Bigsam, Bozho, Breeje, DarkTower, HChang26, SpicyMeatball, Trust, ZanyBonzy, albahaca, bareli, blutorque, grearlake, hals, hassan-truscova, hihen, oualidpro, pfapostol, ravikiranweb3, slvDev, zhaojie
15.328 USDC - $15.33
The _rewardPerToken
function, despite being only utilized for internal logic and calculations within the contract, has been labeled as a public function. This exposes it to external calls and inputs, potentially leading to unexpected contract behaviors. Given the function's critical role in calculating the sensitive value REWARDPERTOKEN
for fairness and reward distribution, this exposure poses a security risk.
function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) public view returns (uint256) { if (totalSupply_ == 0) { return _rewardData[rewardToken].rewardPerTokenStored; } uint256 timeElapsed = lastTimeRewardApplicable_ - _rewardData[rewardToken].lastUpdateTime; uint256 pendingRewardsPerToken = (timeElapsed * _rewardData[rewardToken].rewardRate * 1e18) / totalSupply_; return _rewardData[rewardToken].rewardPerTokenStored + pendingRewardsPerToken; }
REWARDPERTOKEN
by calling the function multiple times with wrong inputs.Manual code analysis.
To enhance the security of the contract and protect the critical internal logic implemented in the _rewardPerToken
function, it is strongly recommended to change the function's visibility to internal. This adjustment ensures that the function can only be called within the current contract or contracts that inherit from it.
Update the function declaration as follows:
function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) internal view returns (uint256) { }
By making this adjustment, you limit external access to the _rewardPerToken
function, reducing the risk of unintended manipulation and ensuring the integrity of the sensitive calculations it performs.
Error
#0 - 0xm3rlin
2024-03-15T12:34:57Z
it is a view function, there is no state change here
#1 - 141345
2024-03-15T13:04:47Z
internal or public, view func, no loss QA is more appropriate
#2 - c4-pre-sort
2024-03-15T13:04:49Z
141345 marked the issue as sufficient quality report
#3 - c4-sponsor
2024-03-17T12:55:58Z
0xCalibur marked the issue as disagree with severity
#4 - 0xCalibur
2024-03-17T12:58:13Z
Ack and fixed here but it's nit/very low one and wasn't really an issue as we validate all rewards token so we use only popular ones. Yet it's cleaner,
here is the fix:
https://github.com/Abracadabra-money/abracadabra-money-contracts/pull/149
#5 - c4-sponsor
2024-03-17T12:58:14Z
0xCalibur (sponsor) acknowledged
#6 - c4-judge
2024-03-29T10:38:26Z
thereksfour changed the severity to QA (Quality Assurance)