TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: medium
Invalid

In `TempleGoldStaking.sol` contract rewards are calculated as distributed even if there are no stakers, locking the rewards forever

Summary:

In TempleGoldStaking.sol contract is a fork of the Synthetix contract, with slight modifications. The code special-cases the scenario where there are no users, by not updating the cumulative rate when the _totalSupply is zero, but it does not include such a condition for the tracking of the timestamp

Vulnerability Details:

function _rewardPerToken() internal view returns (uint256) {
if (totalSupply == 0) {
return rewardData.rewardPerTokenStored;
}
return
rewardData.rewardPerTokenStored +
(((_lastTimeRewardApplicable(rewardData.periodFinish) -
rewardData.lastUpdateTime) *
rewardData.rewardRate * 1e18)
/ totalSupply);
}

Because of this, even when there are no users staking, the accounting logic still thinks funds were being dispersed during that timeframe (because the starting timestamp is updated),

As a result if the distributeRewards() function is called prior to there being any users staking, the funds that should have gone to the first stakers will instead accrue to nobody, and be locked in the contract forever.

Impact:

Non-distributed rewards remain locked within the contract.

Proof of Concept:

Here's an example scenario:

  • Alice is distributionStarter and Bob is a person who wants to stake Temple.

  • Alice calls the distributeRewards() function to mint TGLD for this contract. Let's suppose the minted TGLD is 7*86400 ether to calculate simply.

  • Then rewardRate becomes 1 ether.

  • After 24 hours, Bob stakes 10000 TGLD into the contract.

  • After 6 days, Bob withdraw all staked TGLD and claim rewards. Then he gets 6*86400 ether.

  • As a result, 86400 ether is locked in the contract.

Tools Used:

Manual, Foundry

Recommendations:

In the function distributeRewards(), check if there are enough reward tokens already in the contract.

function distributeRewards() updateReward(address(0)) external {
if (distributionStarter != address(0) && msg.sender != distributionStarter)
{ revert CommonEventsAndErrors.InvalidAccess(); }
// Mint and distribute TGLD if no cooldown set
if (lastRewardNotificationTimestamp + rewardDistributionCoolDown > block.timestamp)
{ revert CannotDistribute(); }
_distributeGold();
uint256 rewardAmount = nextRewardAmount;
if (rewardAmount == 0 ) { revert CommonEventsAndErrors.ExpectedNonZero(); }
nextRewardAmount = 0;
+ if (totalSupply == 0) { revert CommonEventsAndErrors.NoStaker(); }
_notifyReward(rewardAmount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

0xdhanraj30 Submitter
12 months ago
inallhonesty Lead Judge
12 months ago
inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.