Summary
In unstakeVested
, the check that makes sure that the correct epochs are maintained has been implemented incorrectly.
Vulnerability Details
In the unstakeVested()
the conditional check ensures that if the current and NFT-data epochs aren't the same then the funds are locked for 21 days but here lies one extreme case.
function unstakeVested(uint256 _streamID) external checkEpochRollover redeemPendingRewards {
NFTData memory data = _streamIDs[msg.sender][_streamID];
DepositReceipt memory dr = deposits[msg.sender][data.epoch];
if (data.epoch == 0 || data.amount == 0 || dr.vestedStaked == 0 || dr.epoch == 0) {
revert DepositNotFound();
}
if (currentEpoch != data.epoch) {
if (currentEpoch - data.epoch <= lockCycle) revert UnstakeEarly();
}
_unstakeVested(msg.sender, _streamID, data.amount);
}
Here's how this scenario will work out:
USER A vested staked on 1 September 2024 at 2:01 PM
USER A tried to unstake on 22 September 2024 at 2:01 PM(after 21 days)
Result: Txn reverts due to the incorrectly implemented check
Impact
The user won't be able to unstake his funds leading to the bad reputation of the protocol.
Tools Used
Manual Review
Recommendations
function unstakeVested(uint256 _streamID) external checkEpochRollover redeemPendingRewards {
//CHECK
NFTData memory data = _streamIDs[msg.sender][_streamID];
DepositReceipt memory dr = deposits[msg.sender][data.epoch];
if (data.epoch == 0 || data.amount == 0 || dr.vestedStaked == 0 || dr.epoch == 0) {
revert DepositNotFound();
}
// If epoch is same as current epoch then user can unstake immediately
if (currentEpoch != data.epoch) {
// If epoch less than current epoch then user can unstake after at complete lockCycle
- if (currentEpoch - data.epoch <= lockCycle) revert UnstakeEarly();
+ if (currentEpoch - data.epoch < lockCycle) revert UnstakeEarly();
}
_unstakeVested(msg.sender, _streamID, data.amount);
}