Beatland Festival

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

Off-by-One Error in redeemMemorabilia() Prevents Final NFT Minting

Root + Impact

Description

The redeemMemorabilia() function in the FestivalPass contract uses the following condition to check if a memorabilia collection has reached its minting limit:

require(collection.currentItemId < collection.maxSupply, "Collection sold out");

However, this check causes the final item (itemId == maxSupply) to be inaccessible. Once currentItemId reaches maxSupply, the condition fails and no further redemptions are allowed — even though one item remains.

This results in:

  • Under-supply: Fewer NFTs than advertised.

  • Incorrect logic in limited-edition distribution.

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");
BeatToken(beatToken).burnFrom(msg.sender, collection.priceInBeat);
uint256 itemId = collection.currentItemId++;
uint256 tokenId = encodeTokenId(collectionId, itemId);
tokenIdToEdition[tokenId] = itemId;
_mint(msg.sender, tokenId, 1, "");
emit MemorabiliaRedeemed(msg.sender, tokenId, collectionId, itemId);
}

Risk

Likelihood:

  • This will happen automatically when the collection reaches maxSupply - 1 and one more user attempts to redeem the final item.

Impact:

  • Users are unable to mint the final NFT in the collection.

  • Causes the collection to be incomplete, despite expectations and maxSupply.

  • Reduces user trust in NFT supply guarantees.

Proof of Concept

Given:
collection.maxSupply = 100;
collection.currentItemId = 99;
A user calls redeemMemorabilia():
99 < 100: OK, item #99 minted.
collection.currentItemId++now equals 100.
Next user:
100 < 100: Fails. But item #100 should still be allowed

Recommended Mitigation

- require(collection.currentItemId < collection.maxSupply, "Collection sold out");
+ require(collection.currentItemId <= collection.maxSupply, "Collection sold out");

This fix allows the final item (when currentItemId == maxSupply) to be redeemed as expected.

Updates

Lead Judging Commences

inallhonesty Lead Judge 26 days 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.