Beatland Festival

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

Off-by-One Error in redeemMemorabilia Prevents Minting Last NFT in Collection

Root + Itmpact

Description

  • The redeemMemorabilia() function contains an off-by-one error that prevents the final NFT in each memorabilia collection from being minted.

    In the constructor of the MemorabiliaCollection, currentItemId is initialized to 1, and minting continues as long as:

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

Risk

Likelihood:

  • This issue will occur every time a user attempts to redeem the final NFT in any memorabilia collection, since the check fails on the last item (currentItemId == maxSupply).


Impact:

  • Last NFT in each collection cannot be minted

  • Direct loss of 1 unit of revenue per collection

  • Users unable to complete their NFT sets (UX damage)

Proof of Concept

// This test simulates the creation of a memorabilia collection with maxSupply = 2,
// then attempts to mint 2 NFTs from it.
// The first mint succeeds, but the second fails due to the off-by-one check,
// even though there should be 2 NFTs available.
function testOffByOneInRedeemMemorabilia() public {
// Setup: Create collection with maxSupply = 2
vm.startPrank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Test Collection",
"ipfs://test",
1 ether,
2,
true
);
vm.stopPrank();
// Give BEAT tokens to user
vm.startPrank(address(festivalPass));
beatToken.mint(user, 10 ether);
vm.stopPrank();
// User approves and redeems first NFT
vm.startPrank(user);
beatToken.approve(address(festivalPass), 10 ether);
festivalPass.redeemMemorabilia(collectionId); // Works
// Second redemption should succeed, but fails
vm.expectRevert("Collection sold out");
festivalPass.redeemMemorabilia(collectionId); // Reverts
// This confirms the off-by-one logic prevents full minting
vm.stopPrank();
}

Recommended Mitigation

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

Lead Judging Commences

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