Mystery Box

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

Front-Running Vulnerability in MysteryBox Price Changes

Summary

The MysteryBox contract allows the owner to change the price of boxes instantly using the setBoxPrice() function. This implementation is vulnerable to front-running attacks, where users can potentially purchase boxes at a lower price just before a price increase takes effect, or avoid buying just before a price decrease.

Vulnerability Details

The vulnerable code is in the setBoxPrice() function:

@> function setBoxPrice(uint256 _price) public {
require(msg.sender == owner, "Only owner can set price");
@> boxPrice = _price;
}

The issue lies in the immediate price change:

  1. The new price is set instantly in a single transaction.

  2. There is no delay or timelock mechanism to prevent front-running.

  3. The price change is visible in the transaction pool before it's confirmed.

Impact

The impact of this vulnerability is medium:

  1. Economic Loss: The contract owner may lose potential revenue if users exploit this to buy boxes at a lower price just before an intended price increase.

  2. User Frustration: Some users might unknowingly buy at a higher price just before a price decrease, leading to frustration and perceived unfairness.

  3. Market Manipulation: Sophisticated users with access to real-time transaction monitoring could gain an unfair advantage.

  4. Trust Issues: Frequent exploitation could lead to a loss of trust in the fairness of the system.

Tools Used

Manual code review.

Recommendations

To address this vulnerability, consider implementing one or more of the following solutions:

  1. Implement a timelock mechanism for price changes:

contract MysteryBox {
uint256 public boxPrice;
uint256 public nextBoxPrice;
uint256 public priceChangeTimestamp;
uint256 public constant PRICE_CHANGE_DELAY = 1 days;
@> function scheduleBoxPriceChange(uint256 _newPrice) public {
require(msg.sender == owner, "Only owner can schedule price change");
nextBoxPrice = _newPrice;
priceChangeTimestamp = block.timestamp + PRICE_CHANGE_DELAY;
emit BoxPriceChangeScheduled(_newPrice, priceChangeTimestamp);
}
@> function executeBoxPriceChange() public {
require(block.timestamp >= priceChangeTimestamp, "Price change not ready");
require(nextBoxPrice != boxPrice, "No price change scheduled");
boxPrice = nextBoxPrice;
emit BoxPriceChanged(boxPrice);
}
function buyBox() public payable {
require(msg.value == boxPrice, "Incorrect ETH sent");
// ... rest of the function
}
}
  1. Implement a gradual price change mechanism:

contract MysteryBox {
uint256 public boxPrice;
uint256 public targetBoxPrice;
uint256 public lastPriceUpdateTimestamp;
uint256 public constant PRICE_UPDATE_INTERVAL = 1 hours;
uint256 public constant MAX_PRICE_CHANGE_RATE = 5; // 5% per hour
@> function setTargetBoxPrice(uint256 _targetPrice) public {
require(msg.sender == owner, "Only owner can set target price");
targetBoxPrice = _targetPrice;
}
@> function updateBoxPrice() public {
require(block.timestamp >= lastPriceUpdateTimestamp + PRICE_UPDATE_INTERVAL, "Too soon to update");
if (boxPrice < targetBoxPrice) {
uint256 maxIncrease = (boxPrice * MAX_PRICE_CHANGE_RATE) / 100;
boxPrice = min(boxPrice + maxIncrease, targetBoxPrice);
} else if (boxPrice > targetBoxPrice) {
uint256 maxDecrease = (boxPrice * MAX_PRICE_CHANGE_RATE) / 100;
boxPrice = max(boxPrice - maxDecrease, targetBoxPrice);
}
lastPriceUpdateTimestamp = block.timestamp;
emit BoxPriceUpdated(boxPrice);
}
function buyBox() public payable {
updateBoxPrice(); // Ensure price is up-to-date
require(msg.value == boxPrice, "Incorrect ETH sent");
// ... rest of the function
}
}
  1. Use an oracle for price feeds to make price changes more predictable and less susceptible to manipulation.

By implementing one of these solutions, particularly the timelock mechanism, the contract can significantly reduce the risk of front-running during price changes, ensuring a fairer system for all users.

Updates

Appeal created

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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