Omniscia Stakewise Audit

RewardEthToken Code Style Findings

RewardEthToken Code Style Findings

RET-01C: Duplicate Event Emittance & Storage Write

TypeSeverityLocation
Gas OptimizationInformationalRewardEthToken.sol:L216, L253-L259

Description:

The updateTotalRewards function contains a logic path via which the RewardsUpdated event is emitted twice with the same arguments and the lastUpdateBlockNumber is written twice to the same value.

Example:

contracts/tokens/RewardEthToken.sol
207/**
208 * @dev See {IRewardEthToken-updateTotalRewards}.
209 */
210function updateTotalRewards(uint256 newTotalRewards) external override {
211 require(msg.sender == oracles, "RewardEthToken: access denied");
212
213 uint256 periodRewards = newTotalRewards.sub(totalRewards);
214 if (periodRewards == 0) {
215 lastUpdateBlockNumber = block.number;
216 emit RewardsUpdated(0, newTotalRewards, rewardPerToken, 0, 0);
217 }
218
219 // calculate protocol reward and new reward per token amount
220 uint256 protocolReward = periodRewards.mul(protocolFee).div(1e4);
221 uint256 prevRewardPerToken = rewardPerToken;
222 uint256 newRewardPerToken = prevRewardPerToken.add(periodRewards.sub(protocolReward).mul(1e18).div(stakedEthToken.totalDeposits()));
223 uint128 newRewardPerToken128 = newRewardPerToken.toUint128();
224
225 // store previous distributor rewards for period reward calculation
226 uint256 prevDistributorBalance = _balanceOf(address(0), prevRewardPerToken);
227
228 // update total rewards and new reward per token
229 (totalRewards, rewardPerToken) = (newTotalRewards.toUint128(), newRewardPerToken128);
230
231 uint256 newDistributorBalance = _balanceOf(address(0), newRewardPerToken);
232 address _protocolFeeRecipient = protocolFeeRecipient;
233 if (_protocolFeeRecipient == address(0) && protocolReward > 0) {
234 // add protocol reward to the merkle distributor
235 newDistributorBalance = newDistributorBalance.add(protocolReward);
236 } else if (protocolReward > 0) {
237 // update fee recipient's checkpoint and add its period reward
238 checkpoints[_protocolFeeRecipient] = Checkpoint({
239 reward: _balanceOf(_protocolFeeRecipient, newRewardPerToken).add(protocolReward).toUint128(),
240 rewardPerToken: newRewardPerToken128
241 });
242 }
243
244 // update distributor's checkpoint
245 if (newDistributorBalance != prevDistributorBalance) {
246 checkpoints[address(0)] = Checkpoint({
247 reward: newDistributorBalance.toUint128(),
248 rewardPerToken: newRewardPerToken128
249 });
250 }
251
252 lastUpdateBlockNumber = block.number;
253 emit RewardsUpdated(
254 periodRewards,
255 newTotalRewards,
256 newRewardPerToken,
257 newDistributorBalance.sub(prevDistributorBalance),
258 _protocolFeeRecipient == address(0) ? protocolReward: 0
259 );
260}

Recommendation:

We advise a return event to be introduced after the first emittance to ensure that the function ends early and does not waste gas executing the ensuing statements as they will be ineffectual in the case that the periodRewards are zero.

Alleviation:

A return statement was properly introduced according to our recommendation.