Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Memorabilia with maxSupply of 1 Cannot Be Minted

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.

// src/FestivalPass.sol
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:

  • An organizer intends to create a unique, 1-of-1 memorabilia item and logically sets the maxSupply to 1.

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 {
// 1. Organizer creates a collection with maxSupply of 1.
vm.prank(organizer);
uint256 collectionId =
festivalPass.createMemorabiliaCollection("Single Edition", "ipfs://QmSingle", 10e18, 1, true);
// 2. Give a user enough BEAT to redeem.
vm.prank(address(festivalPass));
beatToken.mint(user1, 100e18);
// 3. User attempts to redeem the single item.
vm.prank(user1);
// 4. The call reverts because the check is `currentItemId < maxSupply` (1 < 1 is false).
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);
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Off by one error in redeemMemorabilia

Support

FAQs

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