Trick or Treat

First Flight #27
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Invalid

Incomplete Handling of Half-Price Discount in trickOrTreat Function

Bug Title: Incomplete Handling of Half-Price Discount in trickOrTreat Function

Severity: Medium

Vulnerability Type: Logical Error / Business Logic Flaw

Summary

This report identifies a logical flaw in the trickOrTreat function within the smart contract, which affects the handling of a half-price discount for NFT minting. Users who qualify for this discount are not able to utilize it properly, leading to potential financial loss and decreased user satisfaction.

Vulnerability Details

The trickOrTreat function uses a pseudo-random number generator to determine the price of treats, allowing for the possibility of a half-price offer. However, the current implementation fails to appropriately manage cases where the random number indicates a half-price discount. Instead of allowing users to pay half the required cost, the function may either require the full price or lead to confusion regarding payments. This flaw could allow users to overpay or miss out on discounts they are entitled to.

function trickOrTreat(string memory _treatName) public payable nonReentrant {
Treat memory treat = treatList[_treatName];
require(treat.cost > 0, "Treat cost not set.");
uint256 costMultiplierNumerator = 1;
uint256 costMultiplierDenominator = 1;
// Generate a pseudo-random number between 1 and 1000
uint256 random =
uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, nextTokenId, block.prevrandao))) % 1000 + 1;
if (random == 1) {
// 1/1000 chance of half price (treat) - e.g 1/2 -> 0.5
@> costMultiplierNumerator = 1;
@> costMultiplierDenominator = 2;
} else if (random == 2) {
// 1/1000 chance of double price (trick) - e.g 2/1 -> 2.0
costMultiplierNumerator = 2;
costMultiplierDenominator = 1;
}
@> uint256 requiredCost = (treat.cost * costMultiplierNumerator) / costMultiplierDenominator;
// Logic flaw here: half-price case is not correctly handled.
@> if (costMultiplierNumerator == 2 && costMultiplierDenominator == 1) {
if (msg.value >= requiredCost) {
mintTreat(msg.sender, treat);
} else {
// Mint NFT to contract and store pending purchase.
}
@> } else {
require(msg.value >= requiredCost, "Insufficient ETH sent for treat");
mintTreat(msg.sender, treat);
}
// Refund excess ETH if any.
if (msg.value > requiredCost) {
uint256 refund = msg.value - requiredCost;
(bool refundSuccess,) = msg.sender.call{value: refund}("");
require(refundSuccess, "Refund failed");
}
}

Deploy the contract with the trickOrTreat function.

  • Set up a user address with sufficient ETH balance (e.g., 100 ether).

  • Call trickOrTreat with a valid treat name.

  • If the random number generated is 1, observe that the user is still required to send the full treat cost instead of half.

  • The user should receive a refund only if they overpay, but they are never charged at the discounted rate directly.

Impact

Due to this flaw, users who are entitled to receive a treat at a half-price discount (1/2 multiplier) are not able to benefit from the discount as intended. This results in:

  • Unintended Financial Loss: Users end up paying more than required when they qualify for the half-price discount, potentially causing dissatisfaction and undermining user trust in the platform.

  • Degraded User Experience: The random reward mechanism is not working as expected, which impacts the fairness of the function.

Tools Used

Manual Review

Recommendations

To fix this, the code should include an else if block to handle the half-price scenario: Update the function logic to properly handle the case where random == 1 (half price scenario).

if (costMultiplierNumerator == 2 && costMultiplierDenominator == 1) {
} else if (costMultiplierNumerator == 1 && costMultiplierDenominator == 2) {
require(msg.value >= requiredCost, "Insufficient ETH sent for treat");
mintTreat(msg.sender, treat);
} else {
require(msg.value >= requiredCost, "Insufficient ETH sent for treat");
mintTreat(msg.sender, treat);
}
Updates

Appeal created

bube Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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