TempleGold

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

Next Stakers get benifited as when Staking contract's totalSupply become 0 in any point of time.

Summary

Refer details

Vulnerability Details

totalSupply only updated in 2 places addStake()and _withdrawFor()

And when all token removed from staking contract in any point of time rewardData.rewardPerTokenStorednever reseted, So thats why next stakers could take benefits from this.

Let consider following senario

  • Alice stake 1000 Tokens

  • Fast forward in Time

  • Alice Wants to withdraw all of its token via withdraw()

    • updateReward() modifier called which further call _rewardPerToken()

modifier updateReward(address _account, uint256 _index) {
{
// stack too deep
rewardData.rewardPerTokenStored = uint216(_rewardPerToken());
rewardData.lastUpdateTime = uint40(_lastTimeRewardApplicable(rewardData.periodFinish));
if (_account != address(0)) {
StakeInfo memory _stakeInfo = _stakeInfos[_account][_index];
uint256 vestingRate = _getVestingRate(_stakeInfo);
claimableRewards[_account][_index] = _earned(_stakeInfo, _account, _index);
userRewardPerTokenPaid[_account][_index] = vestingRate * uint256(rewardData.rewardPerTokenStored) / 1e18;
}
}
_;
}
function _rewardPerToken() internal view returns (uint256) {
if (totalSupply == 0) {
return rewardData.rewardPerTokenStored; // @audit-issue what if 1st staking happens then all withdrwal and then again staking happens
}
return
rewardData.rewardPerTokenStored +
(((_lastTimeRewardApplicable(rewardData.periodFinish) -
rewardData.lastUpdateTime) *
rewardData.rewardRate * 1e18)
/ totalSupply);
}

Here you can see that if totalSupply == 0 it returns current stored rewardPerTokenStoredother wise it goes to second step, In our case it will goes through second step and return value from this function will be stored in Storage rewardDatastructure as seen top code segment.

  • As here Alice was single user and he stake withdraw

  • Now rewardDatastruct's rewardPerTokenStoredholds updated value, not get reseted.

  • When next User Bob comes and call for stake() he get benefited from this one, his claimable Reward will as per updateReward()modifier will Use this rewardPerTokenStored as base.

    claimableRewards[_account][_index] = _earned(_stakeInfo, _account, _index);
function _earned(
....
....
uint256 _perTokenReward;
if (vestingRate == 1e18) {
_perTokenReward = _rewardPerToken();
} else {
_perTokenReward = _rewardPerToken() * vestingRate / 1e18;
}
return
(_stakeInfo.amount * (_perTokenReward - userRewardPerTokenPaid[_account][_index])) / 1e18 +
claimableRewards[_account][_index];
}

https://github.com/Cyfrin/2024-07-templegold/blob/main/protocol/contracts/templegold/TempleGoldStaking.sol#L589-L602

https://github.com/Cyfrin/2024-07-templegold/blob/main/protocol/contracts/templegold/TempleGoldStaking.sol#L463-L482

Impact

Later Users May get some extra benifits.

Tools Used

Manual Review

Recommendations

rewardData.rewardPerTokenStoredshould reseted when ever _totalSupplyof staking contract again becomes 0.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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