TempleGold

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

Early Withdrawal of Staked Tokens can loss of Protocol Integrity.

Summary

In the TempleGoldStaking.sol contract, users can stake their tokens for a specified vesting period, during which they earn rewards based on certain calculations. The vesting period implies that users do not have the right to withdraw or sell their tokens until the period ends. This mechanism allows users to stake their tokens, earn rewards during the vesting period, and retrieve their full stake amount at the end.

Vulnerability Details

The withdraw function in the TempleGoldStaking contract allows users to withdraw their staked tokens before the vesting period ends. This capability undermines the entire protocol's functionality, as users no longer need to wait for the vesting period to end and can withdraw their tokens at any time.

Impact

Code Snippet

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

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

// @audit : No check for is the vesting period ends or not.
function withdraw(uint256 amount, uint256 index, bool claim) external override {
StakeInfo storage _stakeInfo = _stakeInfos[msg.sender][index];
_withdrawFor(_stakeInfo, msg.sender, msg.sender, index, amount, claim, msg.sender);
}
function setVestingPeriod(uint32 _period) external override onlyElevatedAccess {
if (_period < WEEK_LENGTH) { revert CommonEventsAndErrors.InvalidParam(); }
// only change after reward epoch ends
if (rewardData.periodFinish >= block.timestamp) { revert InvalidOperation(); }
vestingPeriod = _period;
emit VestingPeriodSet(_period);
}

POC

forge test --mt test_fortis_stakers_CanWithdraw_TokensBefore_VestingPeriodEnds -vvvv
function test_fortis_stakers_CanWithdraw_TokensBefore_VestingPeriodEnds() public {
// for distribution
skip(3 days);
uint32 _rewardDuration = 16 weeks;
// @audit : Vesting period is 16 weeks
_setVestingPeriod(_rewardDuration);
_setRewardDuration(_rewardDuration);
_setVestingFactor(templeGold);
vm.startPrank(alice);
deal(address(templeToken), alice, 1000 ether, true);
_approve(address(templeToken), address(staking), type(uint).max);
uint256 stakeAmount = 100 ether;
uint256 balancebeforestake = templeToken.balanceOf(alice);
staking.stake(stakeAmount);
skip(8 weeks);
// @audit: staker can able to withdraw their staked tokens before vesting period ends .
emit Withdrawn(alice, alice, 1, 100 ether);
staking.withdraw(100 ether, 1, false);
uint256 balanceafterwithdraw = templeToken.balanceOf(alice);
assertEq(balancebeforestake , balanceafterwithdraw );
vm.stopPrank();
}

Tools Used

Foundry

Recommendations

Implement a check in the withdraw function to ensure that users cannot withdraw their tokens before the staking period ends.

uint256 vestfinish;
function setVestingPeriod(uint32 _period) external override onlyElevatedAccess {
if (_period < WEEK_LENGTH) { revert CommonEventsAndErrors.InvalidParam(); }
// only change after reward epoch ends
if (rewardData.periodFinish >= block.timestamp) { revert InvalidOperation(); }
vestingPeriod = _period;
++ vestfinish = block.timestamp + vestingPeriod ;
emit VestingPeriodSet(_period);
}
function withdraw(uint256 amount, uint256 index, bool claim) external override {
++ require(block.timestamp >= vestfinish , "You can't withdraw their staked tokens before vesting period over");
StakeInfo storage _stakeInfo = _stakeInfos[msg.sender][index];
_withdrawFor(_stakeInfo, msg.sender, msg.sender, index, amount, claim, msg.sender);
}
Updates

Lead Judging Commences

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

Support

FAQs

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