Summary
In the MysteryBox.sol::addReward function, the new rewards are added to an array rewardPool. This array is not used anywhere that the user will be able to receive that reward.
Vulnerability Details
When a user has boxes to open and calls openBox, the rewards they can receive are already hard coded in that function. If the owner calls addReward and adds new rewards, these will never be receivable by the users.
Impact
No matter how many times openBox is called, the Diamond Coin reward will never be receivable.
function testNewRewardsCannotBeReceived() public {
vm.prank(owner);
mysteryBox.addReward("Diamond Coin", 2 ether);
MysteryBox.Reward[] memory rewards = mysteryBox.getRewardPool();
assertEq(rewards.length, 5);
assertEq(rewards[4].name, "Diamond Coin");
assertEq(rewards[4].value, 2 ether);
vm.deal(user1, 1 ether);
vm.prank(user1);
mysteryBox.buyBox{value: 0.1 ether}();
console.log("Before Open:", mysteryBox.boxesOwned(user1));
vm.prank(user1);
mysteryBox.openBox();
console.log("After Open:", mysteryBox.boxesOwned(user1));
assertEq(mysteryBox.boxesOwned(user1), 0);
vm.prank(user1);
rewards = mysteryBox.getRewards();
console2.log(rewards[0].name);
assertEq(rewards.length, 1);
}
Tools Used
--Foundry
Recommendations
It is recommended to re-work the openBox function to use the rewardPool array when determining which reward the user will receive.
function openBox() public {
require(boxesOwned[msg.sender] > 0, "No boxes to open");
// Generate a random number between 0 and 99
uint256 randomValue = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100;
// Determine the reward based on probability
if (randomValue < 75) {
// 75% chance to get Coal (0-74)
- rewardsOwned[msg.sender].push(Reward("Coal", 0 ether));
+ rewardsOwned[msg.sender].push(rewardPool[0]);
} else if (randomValue < 95) {
// 20% chance to get Bronze Coin (75-94)
- rewardsOwned[msg.sender].push(Reward("Bronze Coin", 0.1 ether));
+ rewardsOwned[msg.sender].push(rewardPool[1]);
} else if (randomValue < 99) {
// 4% chance to get Silver Coin (95-98)
- rewardsOwned[msg.sender].push(Reward("Silver Coin", 0.5 ether));
+ rewardsOwned[msg.sender].push(rewardPool[2]);
} else {
// 1% chance to get Gold Coin (99)
- rewardsOwned[msg.sender].push(Reward("Gold Coin", 1 ether));
+ rewardsOwned[msg.sender].push(rewardPool[3]);
}
boxesOwned[msg.sender] -= 1;
}