In the staking contract, users can withdraw their staked tokens after a 6-epoch waiting period if unstaking from any of the past epochs, or immediately if unstaking from the current epoch. However, a critical issue arises when users attempt to unstake tokens from earlier epochs while they have previously staked in the current epoch. In this scenario, the contract fails to correctly update the unredeemedEpoch variable. This malfunction results in users losing access to their staked funds and accumulated rewards.
The problem arises in the FjordStaking::unstake()
and FjordStaking::_unstakeVested()
when user requests unstake of fully amount (dr.staked
and dr.vestedStaked
will be 0) from any of the previous epochs, while previously staked in the current epoch. More precisely, in this scenario, userData[userAddress].unredeemedEpoch
is set to 0, even tough user is unstaking from previous epoch and not the current one.
Specifically, when a user attempts to unstake tokens from an earlier epoch (i.e., any epoch other than the current one) after having staked additional tokens in the current epoch, the contract erroneously resets unredeemedEpoch
to 0.
The root cause of the issue lies in the logic that does not differentiate between unstaking from the current epoch and previous epochs, leading to the unintended reset of the unredeemedEpoch
variable.
Place the following test in test/unit/unstake.t.sol:
The impact of this vulnerability is significant, as it prevents users from accessing their staked tokens and accumulated rewards which will remain locked in the FjordStaking
contract. This means that users lose access to newly staked tokens and any rewards that will be earned in the future epochs, potentially leading to a substantial financial loss.
Manual code review / Foundry tests
Add checks that will take into account that unredeemedEpoch
should be set to 0 only if the currentEpoch == _epoch
:
Users that try to unstake tokens from an earlier epoch after they staked in the current epoch will have their ` unredeemedEpoch` set to 0, leading to them being unable to access the newly staked tokens. Impact: High – Tokens are lost Likelihood: Medium – It happens every time a user performs the respective sequence of calls. It’s not always but it’s also not a low likelihood scenario. It’s normal usage of the protocol, that doesn’t necessarily require special conditions.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.