Beatland Festival

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

Unchangeable `MemorabiliaCollection.isActive` Prevents NFTs from Being Sold

Unchangeable MemorabiliaCollection.isActive Prevents NFTs from Being Sold

Description

  • The createMemorabiliaCollection function allows the organizer to set whether the NFT collection can be redeemed or not.

  • However, there is no way to change the isActive property once the collection is created. If a collection is created with activateNow set to false, it becomes permanently unredeemable.

/**
* @audit Below indicates that isActive is for setting the availability of redemption now
* @param isActive Whether redemption is currently enabled for this collection
*/
struct MemorabiliaCollection {
string name;
string baseUri;
uint256 priceInBeat;
uint256 maxSupply;
uint256 currentItemId;
@> bool isActive;
}
function createMemorabiliaCollection(
string memory name,
string memory baseUri,
uint256 priceInBeat,
uint256 maxSupply,
@> bool activateNow
) external onlyOrganizer returns (uint256) {
require(priceInBeat > 0, "Price must be greater than 0");
require(maxSupply > 0, "Supply must be at least 1");
require(bytes(name).length > 0, "Name required");
require(bytes(baseUri).length > 0, "URI required");
uint256 collectionId = nextCollectionId++;
collections[collectionId] = MemorabiliaCollection({
name: name,
baseUri: baseUri,
priceInBeat: priceInBeat,
maxSupply: maxSupply,
currentItemId: 1, // Start item IDs at 1
@> isActive: activateNow
});
emit CollectionCreated(collectionId, name, maxSupply);
return collectionId;
}

Risk

Likelihood:

  • The organizer calls createMemorabiliaCollection with activateNow set to false.

Impact:

  • Users attempting to redeem NFTs from such a collection will always fail, wasting gas. This constitutes an unintended Denial of Service (DoS).

Proof of Concept

The following test shows the organizer creates a collection with an inactive state, and the redemption attempt is reverted. While this is the expected behavior given the isActive state, there is no mechanism to activate the collection later, even if the initial setting was a mistake.

function test_Audit_UnchangeableIsActive() public {
// Setup collection with isActive = false
vm.prank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Limited Shirts",
"ipfs://QmShirts",
50e18,
3,
false
);
// Give users BEAT tokens
vm.startPrank(address(festivalPass));
beatToken.mint(user1, 200e18);
vm.stopPrank();
// User1 redeems item #1 and fails
vm.prank(user1);
vm.expectRevert("Collection not active");
festivalPass.redeemMemorabilia(collectionId);
}

Recommended Mitigation

Add a function to allow the organizer to toggle the isActive status of a memorabilia collection.

+ event CollectionActiveUpdated(uint256 indexed collectionId, bool isActive);
+ function setMemorabiliaCollectionActive(uint256 _collectionId, bool _isActive) external onlyOrganizer {
+ MemorabiliaCollection storage collection = collections[_collectionId];
+ require(collection.priceInBeat > 0, "Collection does not exist");
+ collection.isActive = _isActive;
+ emit CollectionActiveUpdated(_collectionId, _isActive);
+ }
Updates

Lead Judging Commences

inallhonesty Lead Judge 29 days ago
Submission Judgement Published
Validated
Assigned finding tags:

createMemorabiliaCollection with isActive false for later usage - flow not properly implemented.

Low because an organizer can use it with active = true and organizer is trusted.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.