DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Potential Issue with Claim Logic in the FjordStaking Contract

Summary

The FjordStaking contract contains a logic flaw in its claim mechanism, specifically in how it handles user claims for rewards. This flaw can prevent users from initiating new claims after completing a previous one, leading to unnecessary reverts

Vulnerability Details

  1. Initial Claim Check in claimReward:

    if (claimReceipts[msg.sender].requestEpoch > 0 || claimReceipts[msg.sender].requestEpoch >= currentEpoch - 1)
    revert ClaimTooEarly();

    This line checks if the user already has an active claim receipt (requestEpoch > 0). If they do, the function reverts with ClaimTooEarly. This logic is designed to prevent users from making multiple claims within a short period.

  2. Setting requestEpoch in claimReward:

    if (!_isClaimEarly) {
    claimReceipts[msg.sender] = ClaimReceipt({ requestEpoch: currentEpoch, amount: ud.unclaimedRewards });
    emit ClaimReceiptCreated(msg.sender, currentEpoch);
    return (0, 0);
    }

    When a user doesn't claim early (_isClaimEarly == false), a new ClaimReceipt is created, setting requestEpoch to the currentEpoch and storing the unclaimedRewards. This indicates the user has initiated a claim that will be finalized later.

  3. Completing a Claim in completeClaimRequest:

    ClaimReceipt memory cr = claimReceipts[msg.sender];
    if (cr.requestEpoch < 1) revert ClaimReceiptNotFound();
    if (currentEpoch - cr.requestEpoch <= claimCycle) revert CompleteRequestTooEarly();

    The completeClaimRequest function checks if the requestEpoch is valid (greater than 0) and ensures that the claim cycle has passed before allowing the user to finalize the claim and transfer the rewards.

  4. Missing Reset of claimReceipts:
    After completing the claim in completeClaimRequest, the contract does not reset or clear the claimReceipts[msg.sender]:

    claimReceipts[msg.sender];

    This line appears to be a no-op and does not actually reset or modify claimReceipts[msg.sender].

Impact

  • Re-entrant Condition: After a user calls completeClaimRequest, the claim receipt is not reset or cleared. This means that the requestEpoch in claimReceipts[msg.sender] remains greater than 0.

  • Subsequent claimReward Calls: When the user tries to call claimReward again in the future, the check claimReceipts[msg.sender].requestEpoch > 0 will always be true (since it was not reset). This will cause the function to revert with ClaimTooEarly, preventing the user from initiating a new claim.

Tools Used

manual review

Recommendations

reset or delete the claimReceipts after the claim has been completed so that the user can start a new claim process without being blocked by the old receipt.

function completeClaimRequest()
external
checkEpochRollover
redeemPendingRewards
returns (uint256 rewardAmount)
{
ClaimReceipt memory cr = claimReceipts[msg.sender];
//CHECK
if (cr.requestEpoch < 1) revert ClaimReceiptNotFound();
if (currentEpoch - cr.requestEpoch <= claimCycle) revert CompleteRequestTooEarly();
//EFFECT
rewardAmount = cr.amount;
userData[msg.sender].unclaimedRewards -= rewardAmount;
totalRewards -= rewardAmount;
// Clear the claim receipt after completing the claim
delete claimReceipts[msg.sender];
//INTERACT
fjordToken.safeTransfer(msg.sender, rewardAmount);
emit RewardClaimed(msg.sender, rewardAmount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.