Beatland Festival

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

FestivalPass.sol - Off-by-One Error Makes Single-Item Memorabilia Collections Unredeemable

Description

The redeemMemorabilia function contains an off-by-one error that prevents redemption when currentItemId equals maxSupply, making collections with maxSupply = 1 completely unredeemable. The memorabilia redemption logic uses a strict less-than comparison (currentItemId < maxSupply) while currentItemId starts at 1, creating a logical error where single-item collections can never be redeemed because the initial state has currentItemId = 1 and maxSupply = 1, making the condition 1 < 1 always false.

Root Cause

The function uses a strict less-than comparison while currentItemId starts at 1:

currentItemId: 1, // Start item IDs at 1
...
require(collection.currentItemId < collection.maxSupply, "Collection sold out");

For a collection with maxSupply = 1:

  • Initial state: currentItemId = 1, maxSupply = 1

  • Check: 1 < 1 evaluates to false

  • Result: Redemption always fails

Risk

Likelihood: Medium - Organizers will likely create single-item limited edition collections, making this issue inevitable for those specific collections.

Impact: High - Entire memorabilia collections become unusable, preventing users from spending BEAT tokens and breaking organizer's intended functionality.

Impact

Medium severity because:

  • Entire memorabilia collections can be rendered unusable

  • Users cannot redeem limited edition single-item collections

  • Requires redeployment or new collection creation to fix

Proof of Concept

Create a single item collection and try to redeem it

function test_SingleItemCollectionUnredeemable() public {
// Organizer creates a limited edition single-item collection
vm.prank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Limited Edition",
"ipfs://limited",
100e18,
1, // maxSupply = 1 (single item)
true
);
// Give user BEAT tokens
vm.prank(address(festivalPass));
beatToken.mint(user1, 100e18);
// User tries to redeem the single item
vm.prank(user1);
vm.expectRevert("Collection sold out");
festivalPass.redeemMemorabilia(collectionId);
// Collection is permanently broken - no items can ever be redeemed
}

Recommended Mitigation

Change the comparison to less-than-or-equal to allow redemption up to maxSupply:

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...
}

This fix ensures that all items up to and including the maxSupply can be redeemed, making single-item collections functional.

Updates

Lead Judging Commences

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