Description
-
The redeemMemorabilia function checks if a collection is sold out before allowing a user to mint an NFT.
-
The check is require(collection.currentItemId < collection.maxSupply, "Collection sold out"). The currentItemId for a new collection starts at 1. If an organizer creates a collection with a maxSupply of 1 (for a unique 1-of-1 item), this check will always fail, as the condition 1 < 1 is false. This makes it impossible to mint the single item in the collection.
function redeemMemorabilia(uint256 collectionId) external {
MemorabiliaCollection storage collection = collections[collectionId];
require(collection.priceInBeat > 0, "Collection does not exist");
require(collection.isActive, "Collection not active");
@> require(collection.currentItemId < collection.maxSupply, "Collection sold out");
}
Risk
Likelihood:
Impact:
-
The unique, single-edition NFT can never be redeemed or minted.
-
The collection is rendered useless, and any marketing or hype around this unique item is wasted.
Proof of Concept
function test_RedeemMemorabilia_MaxSupplyOne_Fails() public {
vm.prank(organizer);
uint256 collectionId =
festivalPass.createMemorabiliaCollection("Single Edition", "ipfs://QmSingle", 10e18, 1, true);
vm.prank(address(festivalPass));
beatToken.mint(user1, 100e18);
vm.prank(user1);
vm.expectRevert("Collection sold out");
festivalPass.redeemMemorabilia(collectionId);
}
Recommended Mitigation
// In FestivalPass.sol
// Redeem a memorabilia NFT from a collection
function redeemMemorabilia(uint256 collectionId) external {
MemorabiliaCollection storage collection = collections[collectionId];
require(collection.priceInBeat > 0, "Collection does not exist");
require(collection.isActive, "Collection not active");
- require(collection.currentItemId < collection.maxSupply, "Collection sold out");
+ require(collection.currentItemId <= collection.maxSupply, "Collection sold out");
// Burn BEAT tokens
BeatToken(beatToken).burnFrom(msg.sender, collection.priceInBeat);