The claimAllRewards function poses a risk of Denial of Service (DoS) due to potential gas limit exhaustion. This function loops through each entry in the rewardsOwned[msg.sender] array (storage variable), which stores the user's rewards as Reward structs.
In cases where a user has accumulated a large number of rewards over time, the function will consume so much gas that it may exceed the block gas limit, making it impossible for the user to claim their rewards using claimAllRewards.
Furthermore, the associated claimSingleReward and transferReward functions compounds this problem. When users claim a single reward using claimSingleReward or transfers the reward to another user via transferReward, the functions performs a delete operation on the specific array index: delete rewardsOwned[msg.sender][_index];. However, this does not remove the array element or shift subsequent elements to fill the gap. Instead, the element is set to its default values, leaving the array size unchanged. Over time, this can result in a large number of "empty" or default entries, which further bloats the array without reducing its length.
The following gas test was designed to measure gas consumption when claiming all rewards from an array containing a total of 25,000 entries:
The output confirms the issue:
console::log("Gas total consumed after 25000 boxes:", 30289171) [staticcall]
The impact of this vulnerability is high because a user may accumulate so many rewards that it becomes impossible to claim them all due to gas limit exhaustion. Even though the likelihood is low (as users would have to accumulate a significant number of rewards), the consequences are severe for affected users.
Manual Review, VSCode, Foundry
An important optimization to reduce gas consumption in loops is to minimize repeated access to storage variables. By storing the value in a local variable before the loop begins, you can significantly improve efficiency and reduce gas costs:
To further optimize gas usage during increment operations, it's advisable to use uncheckedblocks when there is no risk of overflow, particularly in loops controlled by external logic. This eliminates the cost of Solidity's default overflow checks, providing additional gas savings.
Another possible improvement is to limit the number of rewards a user can accumulate. To do this, consider setting an upper limit on the number of rewards a user can accumulate.
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.