Beatland Festival

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

Memorabilia Collection: Last Item Cannot Be Redeemed Due to Incorrect Supply Bounds Check

Root + Impact

Description

  • The redeemMemorabilia function in FestivalPass.sol contains a logic error that prevents users from redeeming the last item in a memorabilia collection, even when supply is available.

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");
// Burn BEAT tokens
BeatToken(beatToken).burnFrom(msg.sender, collection.priceInBeat);
// Generate unique token ID
uint256 itemId = collection.currentItemId++;
uint256 tokenId = encodeTokenId(collectionId, itemId);
// Store edition number
tokenIdToEdition[tokenId] = itemId;
// Mint the unique NFT
_mint(msg.sender, tokenId, 1, "");
emit MemorabiliaRedeemed(msg.sender, tokenId, collectionId, itemId);
}

Risk

Likelihood:

  • The condition is triggered every time someone tries to redeem the last item in any collection

  • This occurs naturally during normal usage

  • The bug will always trigger when attempting to redeem the final item

Impact:

  • Users cannot redeem the final item in any memorabilia collection

  • Collections effectively have 1 less item than their configured maxSupply

  • This breaks the expected functionality where all items in a collection should be redeemable

Proof of Concept

  1. Create a memorabilia collection with maxSupply = 2

  1. Give a user sufficient BEAT tokens

  1. Successfully redeem the first item

  1. Attempt to redeem the second (and final) item

  1. Expected: Success

  1. Actual: Reverts with "Collection sold out"

// ============ Memorabilia Collection Supply Bounds Issue ============
function test_MemorabiliaCollection_LastItemCannotBeRedeemed() public {
// Create a collection with maxSupply = 2 (only 2 items available)
vm.prank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Test Collection",
"ipfs://test",
100e18,
2, // maxSupply = 2
true
);
// Give user enough BEAT tokens
vm.prank(address(festivalPass));
beatToken.mint(user1, 300e18);
// First redemption should succeed (item #1)
vm.prank(user1);
festivalPass.redeemMemorabilia(collectionId);
// Verify first item was minted
uint256 tokenId1 = festivalPass.encodeTokenId(collectionId, 1);
assertEq(festivalPass.balanceOf(user1, tokenId1), 1, "First item should be minted");
// Second redemption should succeed (item #2) - but it will fail due to the bug
vm.prank(user1);
vm.expectRevert("Collection sold out"); // This should NOT happen!
festivalPass.redeemMemorabilia(collectionId);
// The bug: when currentItemId = 2 and maxSupply = 2
// The condition "currentItemId < maxSupply" becomes "2 < 2" which is false
// But it should be "currentItemId <= maxSupply" which would be "2 <= 2" (true)
}

Recommended Mitigation

Users will be able to redeem the final item in any memorabilia collection

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

Lead Judging Commences

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

Give us feedback!