Description:
The constructor initializes 4 types of rewards along with their names and values. The function openBox however assigns different values to the silver and gold reward types.
rewardPool.push(Reward("Gold Coin", 0.5 ether));
rewardPool.push(Reward("Silver Coin", 0.25 ether));
rewardPool.push(Reward("Bronze Coin", 0.1 ether));
rewardPool.push(Reward("Coal", 0 ether));
if (randomValue < 75) {
rewardsOwned[msg.sender].push(Reward("Coal", 0 ether));
} else if (randomValue < 95) {
rewardsOwned[msg.sender].push(Reward("Bronze Coin", 0.1 ether));
} else if (randomValue < 99) {
rewardsOwned[msg.sender].push(Reward("Silver Coin", 0.5 ether));
} else {
rewardsOwned[msg.sender].push(Reward("Gold Coin", 1 ether));
}
Impact:
Users with gold or silver rewards may expect 0.5 and 0.25 ETH respectively and end up with 1.0 or 0.5 ETH, or vice versa. Basically, users could be confused on what the true value of their rewards are.
Proof of Concept:
function testRewardValuesDiffer() public {
uint256 timestamp = block.timestamp;
for (uint256 i = 0; i < 1000; i++) {
address testAddress = address(uint160(i));
uint256 randomValue = uint256(keccak256(abi.encodePacked(timestamp, testAddress))) % 100;
if (randomValue == 95) {
vm.startPrank(testAddress);
vm.deal(testAddress, 1 ether);
mysteryBox.buyBox{value: 0.1 ether}();
mysteryBox.openBox();
MysteryBox.Reward[] memory rewards = mysteryBox.getRewards();
console2.log(rewards[0].value);
vm.stopPrank();
}
}
}
So the actual reward values come from openBox, which will override the values set in the constructor.
Recommended Mitigation:
Update the values of the rewards in either the constructor or in openBox, so that values match.
+ // example updating constructor to match openBox
- rewardPool.push(Reward("Gold Coin", 0.5 ether));
- rewardPool.push(Reward("Silver Coin", 0.25 ether));
+ rewardPool.push(Reward("Gold Coin", 1.0 ether));
+ rewardPool.push(Reward("Silver Coin", 0.5 ether));
rewardPool.push(Reward("Bronze Coin", 0.1 ether));
rewardPool.push(Reward("Coal", 0 ether));