Beatland Festival

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

A faulty require statement prevents redemption of the final memorabilia item in collections

Description:

The redeemMemorabilia() function contains an off-by-one mathematical error in its boundary check validation. The function uses require(collection.currentItemId < collection.maxSupply) with strict inequality to verify availability. However, the NFT will be minted with the current itemId value because uint256 itemId = collection.currentItemId++ first assigns the current value to itemId, then increments currentItemId. This means when currentItemId equals maxSupply, the check fails even though a valid NFT with that itemId should still be mintable within the collection limit. For collections with maxSupply = 1, no items can ever be redeemed as the initial currentItemId = 1 fails the check 1 < 1. For collections with maxSupply = N, only (N-1) items can be redeemed instead of the intended N items, with the final item remaining permanently inaccessible despite being within the designed supply limit.

Attack path:

  1. Organizer creates a limited memorabilia collection with maxSupply = 1 (unique collectible)

  2. User attempts to redeem the single item via redeemMemorabilia(collectionId)

  3. Function checks require(currentItemId(1) < maxSupply(1)) which evaluates to require(1 < 1)

  4. Condition fails and transaction reverts with "Collection sold out" despite zero items being sold

  5. The single NFT in the collection becomes permanently unredeemable

  6. Similar issue occurs for any collection size - a collection with maxSupply = 5 can only sell 4 items, with the 5th item becoming unredeemable when currentItemId reaches 5

Impact:

Any exclusive or unique memorabilia with maxSupply = 1 becomes completely unusable

All collections lose their final item, reducing actual supply below intended limits

Recommended Mitigation:

Change the boundary check from strict inequality to inclusive inequality:

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");
// ... rest of function logic
}

This allows currentItemId values from 1 to maxSupply (inclusive) to pass validation, enabling redemption of all intended items in each collection.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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.