Mystery Box

First Flight #25
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

Inability to Assign New Rewards in openBox Function

Summary

The openBox function in the smart contract fails to account for dynamically added rewards. When the owner adds a new reward using addReward, users are unable to receive this newly added reward because the openBox function is hardcoded to only distribute the original set of rewards (Coal, Bronze Coin, Silver Coin, and Gold Coin). The current implementation does not accommodate newly added rewards or dynamically update the probabilities, resulting in the new reward being inaccessible to users.

Vulnerability Details

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));
} else if (randomValue < 95) {
// 20% chance to get Bronze Coin (75-94)
rewardsOwned[msg.sender].push(Reward("Bronze Coin", 0.1 ether));
} else if (randomValue < 99) {
// 4% chance to get Silver Coin (95-98)
rewardsOwned[msg.sender].push(Reward("Silver Coin", 0.5 ether));
} else {
// 1% chance to get Gold Coin (99)
rewardsOwned[msg.sender].push(Reward("Gold Coin", 1 ether));
}
boxesOwned[msg.sender] -= 1;
}


POC

function testBug_NewRewardNotAssigned() public {
// Owner adds a new reward (Diamond Coin) with 2 ether value
vm.startPrank(owner);
mysteryBox.addReward("Diamond Coin", 2 ether);
// Verify that the reward was successfully added to the reward pool
MysteryBox.Reward[] memory rewards = mysteryBox.getRewardPool();
assertEq(rewards.length, 5); // Reward pool now contains 5 rewards
assertEq(rewards[4].name, "Diamond Coin");
assertEq(rewards[4].value, 2 ether);
vm.stopPrank();
// User1 buys a mystery box
vm.deal(user1, 2 ether);
vm.startPrank(user1);
mysteryBox.buyBox{value: 0.1 ether}();
// User opens the mystery box
mysteryBox.openBox();
// Check the rewards assigned to the user
MysteryBox.Reward[] memory userRewards = mysteryBox.getRewards();
// Assert that the user did NOT receive the "Diamond Coin" (new reward)
// Since the probabilities are hardcoded in openBox(), the new reward will never be assigned
for (uint256 i = 0; i < userRewards.length; i++) {
require(
keccak256(bytes(userRewards[i].name)) != keccak256(bytes("Diamond Coin")),
"Bug: User received the new reward which should not be possible"
);
}
console2.log("Bug confirmed: User cannot receive newly added rewards due to hardcoded probabilities.");
vm.stopPrank();
}



Impact

user will never receive the newly added reward

Tools Used

Manual Rivew

Recommendations

Dynamic Reward Pool: Replace the hardcoded conditions in openBox with a loop that dynamically checks the cumulative probabilities of all rewards in the rewardPool.

  • Adjustable Probabilities: Allow the owner to specify probabilities for new rewards when they are added, ensuring that the total probabilities always sum to 100%.

Updates

Appeal created

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

addReward won't have any effect on openBox

Support

FAQs

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