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

Incorrect Claim Receipt Mapping Leads to Limited Reward Claiming Functionality

Vulnerability Details

The claimReceipts mapping is incorrectly implemented as a simple mapping from user address to a single ClaimReceipt, instead of the intended nested mapping that would allow multiple claim receipts per user, one for each epoch.

Current implementation:

mapping(address user => ClaimReceipt) public claimReceipts;

Intended implementation:

mapping(address user => mapping(uint16 epoch => ClaimReceipt)) public claimReceipts;

This vulnerability severely limits the functionality of the reward claiming system, preventing users from claiming rewards from specific epochs independently and forcing them into an all-or-nothing claiming strategy.

Proof of Concept (With Example)

Consider the following scenario:

  1. A user stakes tokens in epochs 1, 2, and 3.

  2. The user wants to claim rewards from epoch 1, but leave rewards from epochs 2 and 3 unclaimed.

Current behavior:

function claimReward(bool _isClaimEarly) external checkEpochRollover redeemPendingRewards returns (uint256 rewardAmount, uint256 penaltyAmount) {
UserData storage ud = userData[msg.sender];
// ... (other checks)
if (!_isClaimEarly) {
claimReceipts[msg.sender] = ClaimReceipt({ requestEpoch: currentEpoch, amount: ud.unclaimedRewards });
emit ClaimReceiptCreated(msg.sender, currentEpoch);
return (0, 0);
}
// ... (early claim logic)
}

In this implementation, the user can only create a claim receipt for all unclaimed rewards up to the current epoch. They cannot specify which epoch's rewards they want to claim.

Example:

  1. User has 100 tokens of unclaimed rewards from epoch 1, 150 from epoch 2, and 200 from epoch 3.

  2. When they call claimReward, they are forced to create a claim receipt for all 450 tokens, instead of just the 100 from epoch 1.

Impact

The impact of this vulnerability is significant:

  1. Limited User Control: Users cannot selectively claim rewards from specific epochs, reducing their ability to implement sophisticated reward management strategies.

  2. Forced Claiming Patterns: Users are compelled to claim all rewards at once or nothing, leads to all-or-nothing strategy

Recommended Mitigation

To address this vulnerability, the following steps are recommended:

  1. Modify the claimReceipts mapping to allow multiple claim receipts per user:

    mapping(address user => mapping(uint16 epoch => ClaimReceipt)) public claimReceipts;
  2. Update the claimReward function to accept an epoch parameter, allowing users to specify which epoch they want to claim rewards from:

    function claimReward(uint16 _epoch, bool _isClaimEarly) external checkEpochRollover redeemPendingRewards returns (uint256 rewardAmount, uint256 penaltyAmount) {
    // ... (implementation)
    }
  3. Modify the reward calculation logic to track rewards on a per-epoch basis:

    mapping(address user => mapping(uint16 epoch => uint256)) public unclaimedRewardsByEpoch;
  4. Update the _redeem function to calculate and store rewards for each epoch separately.

  5. Implement a new function to allow users to claim rewards from multiple specific epochs in a single transaction:

    function claimRewardsFromEpochs(uint16[] calldata _epochs, bool _isClaimEarly) external {
    // ... (implementation)
    }
  6. Adjust all related functions (e.g., completeClaimRequest) to work with the new epoch-specific claim receipt structure.

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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