-
Notifications
You must be signed in to change notification settings - Fork 0
Big Denim Viper - Reward Amount can be reduced if notifyRewardAmount is called in mid duration #686
Description
Big Denim Viper
High
Reward Amount can be reduced if notifyRewardAmount is called in mid duration
Summary
The duration reset in _addRewardsForToken will cause reduced rewards for stakers as any user can call notifyRewardAmount mid-period/duration to dilute rewards.
Root Cause
In SymmStaking.sol the _addRewardsForToken function resets the reward period duration to the default value (1 week) instead of using the remaining time from the current period when new rewards are added mid-period.
Anyone can call the notifyRewardAmount function anytime which will dilutes the rewards and the users will receives less reward because of the reward being diluted.
Internal Pre-conditions
-
Admin needs to call configureRewardToken to whitelist a reward token (e.g., USDT).
-
Any user needs to call notifyRewardAmount to initialize a reward period with a non-zero amount.
-
At least sometime should be passed [eg : 3 days]
-
Anyone can call the
notifyRewardAmountfunction and add reward token in the protocol to decrease the rewardRate per token and it will result in users receiving less rewards.
External Pre-conditions
None
Attack Path
- Admin calls
configureRewardTokento enable USDT as a reward token. - Time passes lets assume 3 days after making few deposits by users.
- Someone will call
notifyRewardAmountand add more USDT - Contract will resets duration to 1 week again instead of using remaining 4 days
- Reward rate will drop resulting in user receiving less rewards
Impact
The stakers will suffer losses as per their expected rewards. Attackers can gried the system by intentionally diluting rewards
PoC
No response
Mitigation
- Modify _addRewardsForToken to use remaining time instead of resetting duration:
function _addRewardsForToken(address token, uint256 amount) internal {
TokenRewardState storage state = rewardState[token];
if (block.timestamp >= state.periodFinish) {
state.rate = amount / state.duration;
} else {
uint256 remaining = state.periodFinish - block.timestamp;
uint256 leftover = remaining * state.rate;
- state.rate = (amount + leftover) / state.duration;
+ state.rate = (amount + leftover) / remaining;
+ state.periodFinish = block.timestamp + remaining;
}
state.lastUpdated = block.timestamp;
state.periodFinish = block.timestamp + state.duration;
}
- Restrict notifyRewardAmount: Add access control to prevent unauthorized users from diluting rewards: