function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
@> passSupply[passId] = 0;
}
pragma solidity 0.8.25;
import {Test, console} from "forge-std/Test.sol";
import {FestivalPass} from "../src/FestivalPass.sol";
import {BeatToken} from "../src/BeatToken.sol";
contract PassReconfigurationExploitTest is Test {
FestivalPass public festivalPass;
BeatToken public beatToken;
address public owner;
address public organizer;
address public attacker;
uint256 constant GENERAL_PRICE = 0.05 ether;
uint256 constant VIP_PRICE = 0.1 ether;
uint256 constant BACKSTAGE_PRICE = 0.25 ether;
uint256 constant GENERAL_MAX_SUPPLY = 100;
uint256 constant VIP_MAX_SUPPLY = 50;
uint256 constant BACKSTAGE_MAX_SUPPLY = 10;
function setUp() public {
owner = address(this);
organizer = makeAddr("organizer");
attacker = makeAddr("attacker");
beatToken = new BeatToken();
festivalPass = new FestivalPass(address(beatToken), organizer);
beatToken.setFestivalContract(address(festivalPass));
vm.startPrank(organizer);
festivalPass.configurePass(1, GENERAL_PRICE, GENERAL_MAX_SUPPLY);
festivalPass.configurePass(2, VIP_PRICE, VIP_MAX_SUPPLY);
festivalPass.configurePass(3, BACKSTAGE_PRICE, BACKSTAGE_MAX_SUPPLY);
vm.stopPrank();
vm.deal(attacker, 10 ether);
}
function test_PassReconfigurationExploit() public {
for (uint i = 0; i < GENERAL_MAX_SUPPLY; i++) {
address user = makeAddr(string(abi.encodePacked("user", i)));
vm.deal(user, 1 ether);
vm.prank(user);
festivalPass.buyPass{value: GENERAL_PRICE}(1);
}
assertEq(festivalPass.passSupply(1), GENERAL_MAX_SUPPLY);
vm.prank(attacker);
vm.expectRevert("Max supply reached");
festivalPass.buyPass{value: GENERAL_PRICE}(1);
vm.prank(organizer);
festivalPass.configurePass(1, GENERAL_PRICE, 200);
assertEq(festivalPass.passSupply(1), 0);
vm.prank(attacker);
festivalPass.buyPass{value: GENERAL_PRICE}(1);
assertEq(festivalPass.balanceOf(attacker, 1), 1);
assertEq(festivalPass.passSupply(1), 1);
}
}
function configurePass(uint256 passId, uint256 price, uint256 maxSupply) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
+ require(passSupply[passId] == 0, "Cannot reconfigure after passes minted");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
- passSupply[passId] = 0; // Reset current supply
}