The FjordStaking
contract allows users to stake tokens and earn rewards over time. The _redeem()
function is responsible for calculating and updating user rewards based on their staked amounts. However, the current implementation contains a critical flaw that leads to incorrect reward calculations and unfair distribution of rewards among stakers.
The _redeem()
function calculates rewards using the totalStaked
amount for all epochs since the last claim. This approach fails to account for changes in a user's staked amount over time, leading to inaccurate reward calculations. Specifically, the function does not properly handle intermediate deposits or unstakes, and it updates totalStaked
at the end, which means subsequent calls to _redeem()
will use an incorrect staked amount for previous epochs.
Here's the problematic part of the _redeem()
function:
The main issues are:
It uses the current totalStaked
amount to calculate rewards for all epochs since the last claim, which is incorrect if the staked amount has changed.
It only considers the most recent unredeemed deposit, ignoring any intermediate deposits or unstakes.
It updates totalStaked
at the end, leading to incorrect calculations in subsequent calls.
These issues can result in significant discrepancies in reward distribution, where users who increase their stake over time receive fewer rewards than they should, while users who decrease their stake receive more rewards than they should.
The incorrect reward calculation leads to an unfair distribution of rewards among stakers. Users who frequently adjust their staked amounts (either increasing or decreasing) will receive disproportionate rewards compared to their actual staking behavior.
Stakers who increase their stake frequently may be disincentivized from doing so, as they will receive fewer rewards than they should. Conversely, stakers who decrease their stake may be incentivized to game the system by unstaking and restaking to receive more rewards than they should.
Consider the following scenario:
Alice stakes 100 tokens in epoch 1.
In epoch 5, Alice stakes an additional 100 tokens (total 200).
In epoch 10, Alice unstakes 50 tokens (total 150).
Alice calls _redeem()
in epoch 15.
Current behavior:
The function calculates rewards based on 150 tokens (current totalStaked
) for all epochs from 1 to 14.
Alice receives fewer rewards than she should for epochs 1-9 and more rewards than she should for epochs 10-14.
Correct behavior:
Alice should receive rewards based on 100 tokens for epochs 1-4, 200 tokens for epochs 5-9, and 150 tokens for epochs 10-14.
Manual review
To fix this issue, the contract needs to track staked amounts per epoch and calculate rewards based on the actual staked amount for each epoch. This requires a significant restructuring of the reward calculation and storage mechanism. Here's a suggested approach:
Maintain a mapping of staked amounts per epoch for each user.
Calculate rewards based on the actual staked amount for each epoch, rather than using a single totalStaked
value.
Update the _redeem()
function to iterate through each epoch and calculate rewards based on the staked amount for that specific epoch.
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.