Summary
If any user staked any vested amount at epoch zero it will be locked forever due to unstake-vested misconfigured checks
Vulnerability Details
stakeVested()
currently allows users to stake vested tokens at epoch 0
:
function stakeVested(uint256 _streamID) external checkEpochRollover redeemPendingRewards {
...
DepositReceipt storage dr = deposits[msg.sender][currentEpoch];
if (dr.epoch == 0) {
dr.vestedStaked = _amount;
dr.epoch = currentEpoch;
_activeDeposits[msg.sender].add(currentEpoch);
} else {
dr.vestedStaked += _amount;
}
_streamIDs[msg.sender][_streamID] = NFTData({ epoch: currentEpoch, amount: _amount });
_streamIDOwners[_streamID] = msg.sender;
newStaked += _amount;
newVestedStaked += _amount;
sablier.transferFrom({ from: msg.sender, to: address(this), tokenId: _streamID });
points.onStaked(msg.sender, _amount);
emit VestedStaked(msg.sender, currentEpoch, _streamID, _amount);
}
The problem lies in unstaking this vested amount using unstakeVested
as this function has implemented a check that reverts on every DepositReceipt
call with Epoch==0
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);
}
Even in unstakeAll()
there is a continue statement that simply skips the receipts with zero epoch values.
function unstakeAll()
external
checkEpochRollover
redeemPendingRewards
returns (uint256 totalStakedAmount)
{
uint256[] memory activeDeposits = getActiveDeposits(msg.sender);
if (activeDeposits.length == 0) revert NoActiveDeposit();
for (uint16 i = 0; i < activeDeposits.length; i++) {
uint16 epoch = uint16(activeDeposits[i]);
DepositReceipt storage dr = deposits[msg.sender][epoch];
if (dr.epoch == 0 || currentEpoch - epoch <= lockCycle) continue;
totalStakedAmount += dr.staked;
...
}
Impact
The staked amount will be locked forever
Tools Used
Manual Review
Recommendations
Configure the checks properly