Vulnerability Details
The NatSpec documentation for FjordStaking::unstake and FjordStaking::unstakeVested indicates that the function claims the rewards and also additionally it is also mentioned that _isClaimEarly if set to true than they can withdraw the rewards beforehand. But these functions just handle the unstaking of the staked tokens where just the tokens of the users are unstaked.This leads us to conclude that the vulnerability stems from incorrect NatSpec documentation. However, this conclusion should be verified by the protocol development team.
@>
@>
@>
function unstake(
uint16 _epoch,
uint256 _amount
) external checkEpochRollover redeemPendingRewards returns (uint256 total) {
if (_amount == 0) revert InvalidAmount();
DepositReceipt storage dr = deposits[msg.sender][_epoch];
.........
@>
@>
@>
function unstakeVested(
uint256 _streamID
) external checkEpochRollover redeemPendingRewards {
NFTData memory data = _streamIDs[msg.sender][_streamID];
DepositReceipt memory dr = deposits[msg.sender][data.epoch];
..............
Impact
The incorrect NatSpec documentation in the FjordStaking::unstake and FjordStaking::unstakeVested functions could lead to significant confusion for users,developers and auditors relying on the documentation to understand the protocol's behavior. The documentation suggests that these functions handle both unstaking and claiming rewards, with an additional option to claim rewards early when _isClaimEarly is set to true. However, the functions only perform the unstaking of tokens without managing reward claims.
Tools Used
Manual
Recommendations
/// @notice Unstake FJORD tokens from the contract.
/// @dev This function allows users to unstake a certain number of FJORD tokens,
- /// while also claiming all the pending rewards. If _isEarly is true then the
- /// user will be able to bypass rewards cooldown of 3 epochs and claim early,
- /// but will incur early claim penalty.
/// @param _epoch The epoch cycle from which user wants to unstake.
/// @param _amount The amount of tokens user wants to unstake.
/// @return total The total amount sent to the user.
function unstake(
uint16 _epoch,
uint256 _amount
) external checkEpochRollover redeemPendingRewards returns (uint256 total) {
if (_amount == 0) revert InvalidAmount();
DepositReceipt storage dr = deposits[msg.sender][_epoch];
.........
/// @notice Unstake vested FJORD tokens from the contract.
/// @dev This function allows users to unstake vested FJORD tokens,
- /// while also claiming all the pending rewards. If _isClaimEarly is true then the
- /// user will be able to bypass rewards cooldown of 3 epochs and claim early,
- /// but will incur early claim penalty.
/// @param _streamID The sablier streamID that the user staked.
function unstakeVested(
uint256 _streamID
) external checkEpochRollover redeemPendingRewards {
//CHECK
NFTData memory data = _streamIDs[msg.sender][_streamID];
DepositReceipt memory dr = deposits[msg.sender][data.epoch];
..............