Summary
In openBox function, the reward values specifically for "Silver Coin" and "Gold Coin" are wrongly stated causing the protocol in risk of financial loss and potentially in the dilemma of not having sufficient funds based on initial economic model to pay the rewards when users make the claim of their rewards
https://github.com/Cyfrin/2024-09-mystery-box/blob/281a3e35761a171ba134e574473565a1afb56b68/src/MysteryBox.sol#L56-L61
Vulnerability Details
During the deployment of the contract, the constructor tapout the rewardPool for "Gold Coin", 0.5 ether and "Silver Coin", 0.25 ether
constructor() payable {
owner = msg.sender;
boxPrice = 0.1 ether;
require(msg.value >= SEEDVALUE, "Incorrect ETH sent");
<@@>! 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));
}
However, in the openBox function, the rewards that granted to users when they hit the required random value were found wrong
function openBox() public {
require(boxesOwned[msg.sender] > 0, "No boxes to open");
uint256 randomValue = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100;
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));
}
boxesOwned[msg.sender] -= 1;
}
Silver Coin was wrongly rewarded with 0.5 ether as opposed to the actual value of 0.25 ether and Gold Coin was wrongly rewarded with 1 ether as opposed to actual value of 0.5 ether. These two coin categories were rewarded double of the actual reward value resulting a great loss to the protocol designed economic model and potentially causes the protocol in dilemma of insufficient funds to pay the rewards to users when they make their claims.
Impact
Protocol in risk of great loss from its intended designed economic model and potentially in dilemma of insufficient funds to pay user rewards when users make their claims.
Tools Used
Manual review
Recommendations
Make corrections in openBox so the reward value for Silver Coin and Gold Coin were adjusted to the correct values aligning to the rewardPool values during the contract deployment.
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));
+ rewardsOwned[msg.sender].push(Reward("Silver Coin", 0.25 ether));
} else {
// 1% chance to get Gold Coin (99)
- rewardsOwned[msg.sender].push(Reward("Gold Coin", 1 ether));
+ rewardsOwned[msg.sender].push(Reward("Gold Coin", 0.5 ether));
}
boxesOwned[msg.sender] -= 1;
}