Proud Tartan Lynx
Medium
Even if someone's new earningPower
is zero and they have no rewards, they can still receive rewards amounting to maxBumpTip - claimFeeParameters.feeAmount
.
https://github.com/sherlock-audit/2024-11-tally/blob/main/staker/src/GovernanceStaker.sol#L497
maxBumpTip
>claimFeeParameters.feeAmount
.
N/A
- A malicious user has
n
stakes, each with an amount of1
. - Wait untill all new
earningPower
is zero. - The user's unclaimedRewards will reach
n * maxBumpTip
soon after theearningPower
of others updates to zero. - The user can claim rewards totaling
n * (maxBumpTip - claimFeeParameters.feeAmount)
Users can obtain rewards without earningPower
.
If the new totalEarningPower
is zero, even if their deposit amount is 1
, they can soon receive rewards amounting to maxBumpTip - claimFeeParameters.feeAmount
.
If there are many stakes with an amount of 1
, the protocol must send the rewards amounting to maxBumpTip - claimFeeParameters.feeAmount
to each one.
This results in a loss for the protocols.
GovernanceStaker.sol
471: function bumpEarningPower(
DepositIdentifier _depositId,
address _tipReceiver,
uint256 _requestedTip
) external virtual {
if (_requestedTip > maxBumpTip) revert GovernanceStaker__InvalidTip();
Deposit storage deposit = deposits[_depositId];
_checkpointGlobalReward();
_checkpointReward(deposit);
uint256 _unclaimedRewards = deposit.scaledUnclaimedRewardCheckpoint / SCALE_FACTOR;
(uint256 _newEarningPower, bool _isQualifiedForBump) = earningPowerCalculator.getNewEarningPower(
deposit.balance, deposit.owner, deposit.delegatee, deposit.earningPower
);
if (!_isQualifiedForBump || _newEarningPower == deposit.earningPower) {
revert GovernanceStaker__Unqualified(_newEarningPower);
}
if (_newEarningPower > deposit.earningPower && _unclaimedRewards < _requestedTip) {
revert GovernanceStaker__InsufficientUnclaimedRewards();
}
// Note: underflow causes a revert if the requested tip is more than unclaimed rewards
497: if (_newEarningPower < deposit.earningPower && (_unclaimedRewards - _requestedTip) < maxBumpTip)
{
revert GovernanceStaker__InsufficientUnclaimedRewards();
}
// Update global earning power & deposit earning power based on this bump
totalEarningPower =
_calculateTotalEarningPower(deposit.earningPower, _newEarningPower, totalEarningPower);
depositorTotalEarningPower[deposit.owner] = _calculateTotalEarningPower(
deposit.earningPower, _newEarningPower, depositorTotalEarningPower[deposit.owner]
);
deposit.earningPower = _newEarningPower.toUint96();
// Send tip to the receiver
SafeERC20.safeTransfer(REWARD_TOKEN, _tipReceiver, _requestedTip);
deposit.scaledUnclaimedRewardCheckpoint =
deposit.scaledUnclaimedRewardCheckpoint - (_requestedTip * SCALE_FACTOR);
}
When the user's new earningPower
is zero and _unclaimedRewards
is less than maxBumpTip
, the bumpEarningPower
function always reverts.
Thus, the user's _unclaimedRewards
can reach maxBumpTip
, allowing them to claim maxBumpTip - claimFeeParameters.feeAmount
794: function _setMaxBumpTip(uint256 _newMaxTip) internal virtual {
emit MaxBumpTipSet(maxBumpTip, _newMaxTip);
+ require(_newMaxTip <= claimFeeParameters.feeAmount,"");
maxBumpTip = _newMaxTip;
}