Description: There are three instances where delete operation is performed on rewardsOwned in MysteryBox contract. delete operation sets the element at that specified index to the default value for the Reward struct (in this case, an empty Reward struct with a name of an empty string and a value of 0). The mapping still retains its full length, and the element at _index will simply be an empty reward.
Impact: Having empty entries can lead to the below issues.
Gaps in the Mapping, resulting in:
Wasted space: The array still holds the empty element, which consumes memory and gas unnecessarily.
Incorrect logic and gas inefficiency: The other parts of the contract (e.g., loops or functions) that iterate over the rewardsOwned array will encounter empty reward slots and may behave incorrectly. For instance, if a user has already claimed all of his boxes reward, calling MysterBox::claimAllRewards will still loop through all of his empty rewards and cost gas.
Proof of Concept:
User A purchases 5 boxes by calling MysteryBox::buyBox function.
He then opens these boxes by MysteryBox::openBox function.
He either transfers some of the boxes to user B by using MysteryBox::transferReward function or claims the reward for himself by MysteryBox::claimSingleReward or MysteryBox::claimAllRewards.
When he calles MysteryBox::getRewards function, he will still get 5 entries returned. Although the entries for the claimed/transferred reward should have been removed.
Proof of Code
Place the folowing into TestMysteryBox.t.sol
Recommended Mitigation: Use pop instead of delete wherever a single entry is supposed to be removed from the mapping.
Update MysteryBox::transferReward function:
Update MysteryBox::claimSingleReward function:
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.