TempleGold

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

_getVestingRate will be wrong if `block.timestamp==_stakeInfo.fullyVestedAt`

Summary

The _getVestingRate() function will return an incorrect value if block.timestamp == _stakeInfo.fullyVestedAt. When the current time matches fullyVestedAt, the function should return 1e18 instead of computing the vesting rate.

Vulnerability Details

The staking contract uses the _getVestingRate() function to determine the staking rate based on the current time. It also supports the time at which a user's staking will be fully vested, storing this value in the staking struct. The fullyVestedAt value is derived from block.timestamp + vestingPeriod. However, since the vestingPeriod can be changed, it is possible for the state variable vestingPeriod and stakeInfo.fullyVestedAt to have different values. This discrepancy can cause issues and remains a valid concern.

function _getVestingRate(StakeInfo memory _stakeInfo) internal view returns (uint256 vestingRate) {
if (_stakeInfo.stakeTime == 0) {
return 0;
}
if (block.timestamp > _stakeInfo.fullyVestedAt) { // @audit : off by one it should be >=
vestingRate = 1e18;
} else {
vestingRate = (block.timestamp - _stakeInfo.stakeTime) * 1e18 / vestingPeriod;
}
}

POC:

Add following test case to TempleGoldStaking.t.sol contract :

function test_getReward_tgldStaking_exact_at_Vested_time() public {
// for distribution
skip(3 days);
uint32 _rewardDuration = 16 weeks;
_setVestingPeriod(_rewardDuration);
_setRewardDuration(_rewardDuration);
_setVestingFactor(templeGold);
vm.startPrank(bob);
deal(address(templeToken), bob, 1000 ether, true);
_approve(address(templeToken), address(staking), type(uint).max);
uint256 stakeAmount = 100 ether;
staking.stake(stakeAmount);
vm.stopPrank();
_setVestingPeriod(_rewardDuration+2 days); // @audit : this Issue would arise when the vesting Peroid has chnged than the one store woth staking record
uint256 stakeTime = block.timestamp;
uint256 goldBalanceBefore = templeGold.balanceOf(address(staking));
staking.distributeRewards();
uint256 goldRewardsAmount = templeGold.balanceOf(address(staking)) - goldBalanceBefore;
ITempleGoldStaking.StakeInfo memory _stakeInfo = staking.getAccountStakeInfo(bob, 1);
vm.warp(_stakeInfo.fullyVestedAt); //
uint256 earnedAtFullVested = staking.earned(bob, 1); // here the user should be able to claim all tokens becuse it is claiming at fullyVestedAt
vm.warp(_stakeInfo.fullyVestedAt + 1);
uint256 earnedAfterFullVested = staking.earned(bob, 1);
assertGt(earnedAfterFullVested,earnedAtFullVested);
}

run with the command : forge test --mt test_getReward_tgldStaking_exact_at_Vested_time.

Impact

The user will receive fewer reward tokens than expected at the fully vested time if the vestingPeriod differs from the value stored in the user's staking record.

Tools Used

Manual Review

Recommendations

change the following condition in _getVestingRate:

-- if (block.timestamp > _stakeInfo.fullyVestedAt) {
++ if (block.timestamp >= _stakeInfo.fullyVestedAt) {
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

0xaman Submitter
11 months ago
inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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