Summary
TempleGoldStaking::distributeReward()
function is used to distribute TGLD emission rewards minted to the TempleGoldStaking
contract to stakers. It starts another epoch of rewards distribution and calculates new rewardRate
from any left over rewards up until now. Despite performing major state changes, it doesn't emit a corresponding event.
Vulnerability Details
It is an unskippable best practice to emit an event whenever a state change happens to a contract. TempleGoldStaking::distributeRewards()
calls TempleGoldStaking::_notifyReward()
function which in turn perform operations and update the nextRewardAmount
and rewardData
state variables. Despite making crucial state changes, the function doesn't emit an event.
Impact
As events are used by off-chain services to track contract activities, incorrect event emission may cause unforseen discrepancies. Despite perhaps not seeing the need to emit events in some cases at the moment, it is always better to do so since contracts are immutable but the need to index such events may come up at any time in the future.
Recommendations
Update TempleGoldStaking::distributeRewards()
to emit a suitable event and append it to the events list in ITempleGoldStaking
.
interface ITempleGoldStaking {
event GoldDistributionNotified(uint256 amount, uint256 timestamp);
event Staked(address indexed staker, uint256 amount);
event MigratorSet(address migrator);
event Withdrawn(address indexed staker, address to, uint256 stakeIndex, uint256 amount);
event RewardDistributionCoolDownSet(uint160 cooldown);
event DistributionStarterSet(address indexed starter);
event VestingPeriodSet(uint32 _period);
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
event RewardPaid(address indexed staker, address toAddress, uint256 index, uint256 reward);
event RewardDurationSet(uint256 duration);
+ event RewardsDistributed(uint256 rewardAmount);
}
function distributeRewards() updateReward(address(0), 0) external {
if (distributionStarter != address(0) && msg.sender != distributionStarter)
{ revert CommonEventsAndErrors.InvalidAccess(); }
if (totalSupply == 0) { revert NoStaker(); }
// Mint and distribute TGLD if no cooldown set
if (lastRewardNotificationTimestamp + rewardDistributionCoolDown > block.timestamp)
{ revert CannotDistribute(); }
_distributeGold();
uint256 rewardAmount = nextRewardAmount;
// revert if next reward is 0 or less than reward duration (final dust amounts)
if (rewardAmount < rewardDuration ) { revert CommonEventsAndErrors.ExpectedNonZero(); }
nextRewardAmount = 0;
_notifyReward(rewardAmount);
lastRewardNotificationTimestamp = uint32(block.timestamp);
+ emit RewardsDistributed(rewardAmount);
}
Tools Used
Manual Review.