Summary
User can send an empty reward to another user. `delete rewardsOwned[msg.sender][_index]` this resets the struct properties in Reward struct and don't delete it from the array of `rewardsOwned[msg.sender]`. For example user1 buy a box, then open it and then transfer it to user2. After that `rewardsOwned[user1][0]` will be `Reward{name: "", value: 0}` because `delete' resets the properties. If use1 tries to transfer again `rewardsOwned[user1][0]` reward it will work, therefore user2 is going to have 2 rewards an one of them is going to be empty (value: 0, name: "").
Impact
User can have many empty rewards and when tries to invoke `MysteryBox::claimAllRewards` function, the amount of gas user has to pay will be much bigger because `MysteryBox::claimAllRewards` function loops trough all rewards of an user.
Tools Used
Proof of concept using solidity and foundry. Add the following in TestMysteryBox.t.sol
:
```javascript
function testTransferRewards_AddEmptyReward() public {
vm.deal(user1, 0.1 ether);
vm.startPrank(user1);
mysteryBox.buyBox{value: 0.1 ether}();
mysteryBox.openBox();
mysteryBox.transferReward(user2, 0);
vm.stopPrank();
uint256 expectedRewardsLength = 1;
vm.prank(user2);
uint256 actualRewardsLength = mysteryBox.getRewards().length;
assertEq(actualRewardsLength, expectedRewardsLength);
vm.startPrank(user1);
mysteryBox.transferReward(user2, 0);
vm.stopPrank();
vm.prank(user2);
uint256 actualRewardsLength2 = mysteryBox.getRewards().length;
assert(actualRewardsLength2 != expectedRewardsLength);
}
```
Recommendations
```diff
+ uint256 rewardsLength = rewardsOwned[msg.sender].length;
+ if (rewardsLength > 0 && rewardsLength - 1 != _index) {
+ uint256 lastRewardIndex = rewardsLength - 1;
+ Reward memory temp = rewardsOwned[msg.sender][lastRewardIndex];
+ rewardsOwned[msg.sender][lastRewardIndex] = rewardsOwned[msg.sender][_index];
+ rewardsOwned[msg.sender][_index] = temp;
+ }
+ rewardsOwned[msg.sender].pop();
- delete rewardsOwned[msg.sender][_index];
```