TempleGold

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

`vestingRate` of `TempleGoldStaking` may be inflated when `vestingPeriod` decreases.

Summary

In the case of vestingPeriod decreases, vestingRate may be inflated and and can be larger than 1e18.
This inflates the rewards to the stakers and causes the lack of reward tokens.

Vulnerability Details

The relevant code for TempleGoldStaking.setVestingFactor() is as follows:

function _getVestingRate(StakeInfo memory _stakeInfo) internal view returns (uint256 vestingRate) {
if (_stakeInfo.stakeTime == 0) {
return 0;
}
if (block.timestamp > _stakeInfo.fullyVestedAt) {
vestingRate = 1e18;
} else {
491: vestingRate = (block.timestamp - _stakeInfo.stakeTime) * 1e18 / vestingPeriod;
}
}

The formula on L491 is based on the assumption that _stakeInfo.fullyVestedAt - _stakeInfo.stakeTime == vestingPeriod.
However, when administrator decreases vestingPeriod by calling of TemplateGoldStaking.setVestingPeriod, _stakeInfo.fullyVestedAt - _stakeInfo.stakeTime becomes greater than vestingPeriod and thus the vestingRate may be greater than 1e18.

The vestingRate is used to calculate the rewards amount for stakers in TempleGoldStaking._earned() as follows.

function _earned(
StakeInfo memory _stakeInfo,
address _account,
uint256 _index
) internal view returns (uint256) {
uint256 vestingRate = _getVestingRate(_stakeInfo);
if (vestingRate == 0) {
return 0;
}
uint256 _perTokenReward;
if (vestingRate == 1e18) {
_perTokenReward = _rewardPerToken();
} else {
476: _perTokenReward = _rewardPerToken() * vestingRate / 1e18;
}
return
480: (_stakeInfo.amount * (_perTokenReward - userRewardPerTokenPaid[_account][_index])) / 1e18 +
claimableRewards[_account][_index];
}

Therefore, if vestingRate is greater than 1e18, the rewards amount to the stakers will be inflated.

Scenario:

  1. At the begining, vestingPeriod is 3 weeks.

  2. A staker stake some tokens and 2 weeks elasped.

  3. Administrator decreases vestingPeriod to 1 weeks.

  4. The vestingRate of the staker is calculated as ((block.timestamp - _stakeInfo.stakeTime) * 1e18 / vestingPeriod) = (2 weeks * 1e18 / 1 weeks) = 2e18 in L491.

  5. The staker calls TempleGoldStaking.getReward() and takes away twice rewards from the staking pool.

  6. This may cause lacks of reward tokens from TempleGoldStaking.

Impact

When administrator decreases vestingPeriod, the rewards to the stakers may be inflated and the lack of reward tokens may occur.

Code snippet

Tools Used

Manual Review

Recommendations

Modify the TempleGoldStaking._getVestingRate() as follows.

function _getVestingRate(StakeInfo memory _stakeInfo) internal view returns (uint256 vestingRate) {
if (_stakeInfo.stakeTime == 0) {
return 0;
}
if (block.timestamp > _stakeInfo.fullyVestedAt) {
vestingRate = 1e18;
} else {
- vestingRate = (block.timestamp - _stakeInfo.stakeTime) * 1e18 / vestingPeriod;
+ vestingRate = (block.timestamp - _stakeInfo.stakeTime) * 1e18 / (_stakeInfo.fullyVestedAt - _stakeInfo.stakeTime);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Changes to vesting period is not handled inside `_getVestingRate`

Support

FAQs

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