Users have the ability to stake Sablier NFT, streaming Fjord tokens
to earn rewards.
The effectively staked amount is the amount of tokens deposited in the stream minus the amount of tokens withdrawn by the user. The refundedAmount
can be omitted as it will always equal 0 unless the stream is effectively cancelled.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L414
Authorized Fjord multisig addresses have the ability to cancel a stream that is staked which will reduce the staked amount of the user by the amount of tokens that have not been streamed yet.
Cancelling a staked stream will trigger the onStreamCanceled()
callback which in turn will trigger _unstakeVested()
responsible for reducing the user's staked amount.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L819-L840
Users have the ability to use claimRewards()
to start a claiming process that will take at least 3 weeks (3 epochs).
This process will save the rewards owed to the user in the claimReceipts
storage variable. The amount owed is calculated based upon the amount of tokens the user has staked.
The issue is that a user has the ability to trigger the claiming process right before his staked stream is cancelled. Effectively claiming rewards as if he staked the entire stream while in reality he only partially staked his stream.
Let the following scenario :
user stakes a Sablier NFT that is streaming 1000 tokens
multiple epochs pass and the user has the ability to claim rewards but keeps waiting
an authorized Fjord multisig address attempts to cancel the stream when it has 500 tokens left to be streamed
the user frontruns this transaction and calls claimRewards()
which will register the owed rewards for 1000 tokens
the stream is cancelled and Fjord retrieves his 500 tokens
after 3 epochs, the user can call completeClaimRequest()
to get his rewards for staking 1000 tokens while he effectively only staked 500 of his tokens
A user has the ability to claim an amount of rewards that does not align with the amount of tokens that was effectively staked.
Manual review
When a Sablier stream is cancelled, make sure the _unstakeVested()
function updates the claimReceipts
variable representing the user's unclaimedRewards
to account for the amount of tokens retrieved by Fjord.
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.