The onStreamCanceled function is triggered by the Sablier contract to unstake a stream owner's vested FJORD tokens. It begins by calling _redeem to update the streamOwner’s rewards, then proceeds with _unstakeVested to partially or fully unstake the vested FJORD tokens.
Here's the _unstakeVested where msg.sender is used instead of streamOwner.
As the onStreamCanceled function is always called by the Sablier, it's wrong to use msg.sender in the _unstakeVested function.
Hence, the onUnstaked function incorrectly assigns the Sablier address to the user variable, causing the function to revert because the original staking points were correctly assigned to the streamOwner.
Since the onStreamCanceled function is executed within a try-catch block, this revert leads to the points.onUnstaked call being bypassed. Consequently, the user's totalStaked in the FjordPoints contract remains unchanged, enabling them to continue earning points on the unstaked amount and receiving more rewards than they should.
The explicit streamOwner will gain more rewards than they should, and this can also be used to exploit the system to earn extra rewards.
Manual Review
To resolve this issue, ensure that the streamOwner is passed to the points.onUnstaked function instead of msg.sender. This will correctly attribute the unstaking to the streamOwner and adjust their points accordingly.
Indeed the `points.onUnstaked` should use the streamOwner instead of msg.sender as an input parameter. Impact: high - The vested stakers who got their streams canceled will keep on receiving rewards (points included) for the previously staked stream. Likelihood: low - whenever a Sablier stream sender decides to `cancel()` a recipient's stream
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.