The _checkEpochRollover function in the FjordStaking contract suffers from a precision loss issue when calculating pendingRewardsPerToken. This occurs due to the division of a small pendingRewards value by a large totalStaked value, resulting in zero reward distribution under certain conditions.
uint256 pendingRewardsPerToken = (pendingRewards * PRECISION_18) / totalStaked;
Setup:
Deploy the FjordStaking contract with a large number of tokens staked (totalStaked).
Ensure pendingRewards is set to a very small value.
Execution:
Call the addReward function with a minimal reward amount, triggering _checkEpochRollover.
Observation:
Calculate pendingRewardsPerToken as (pendingRewards * PRECISION_18) / totalStaked.
If pendingRewards is significantly smaller than totalStaked, the result of this division may be zero due to integer division.
Proof:
With totalStaked = 1,000,000,000 and pendingRewards = 1, the calculation yields:
pendingRewardsPerToken = (1 * 1e18) / 1e9 = 1e9.
If pendingRewards is even smaller, the result can be zero, leading to no rewards being distributed to stakers for that epoch.
Users may receive no rewards for an epoch despite there being pending rewards.
Malicious actors could manipulate the reward distribution by ensuring pendingRewards remains small relative to totalStaked, effectively nullifying rewards for honest stakers.
Manual review
Consider scaling pendingRewards before division to maintain precision, then scale down the result.
Implement a check to ensure that pendingRewards is above a certain threshold before proceeding with the division. This can prevent the calculation from resulting in zero.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.