Inactive Collections — Indefinite BEAT Lock-up
Description
-
Normal behaviour: Organizer creates memorabilia collections with activateNow = true
so users can immediately redeem BEAT for NFTs.
-
Issue: Collections can be created with activateNow = false
and there is no mechanism to activate them later, nor any timeout. Users may acquire BEAT expecting to redeem specific memorabilia, but the organizer can indefinitely prevent access.
function createMemorabiliaCollection(..., bool activateNow) external onlyOrganizer {
@> collections[collectionId] = MemorabiliaCollection({
isActive: activateNow
});
}
function redeemMemorabilia(uint256 collectionId) external {
@> require(collection.isActive, "Collection not active");
}
Risk
Likelihood:
Impact:
Proof of Concept
function test_CollectionNeverActivated() public {
vm.prank(alice);
festivalPass.buyPass{value: 0.1 ether}(VIP_PASS);
vm.prank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Limited Edition", "ipfs://limited", 3e18, 100, false
);
vm.prank(alice);
vm.expectRevert("Collection not active");
festivalPass.redeemMemorabilia(collectionId);
vm.warp(block.timestamp + 365 days);
vm.prank(alice);
vm.expectRevert("Collection not active");
festivalPass.redeemMemorabilia(collectionId);
assertEq(beatToken.balanceOf(alice), 5e18, "Alice holds 'useless' BEAT");
}
Recommended Mitigation
+ mapping(uint256 => bool) public collectionActivated;
+ function activateCollection(uint256 collectionId) external onlyOrganizer {
+ require(collections[collectionId].priceInBeat > 0, "Collection does not exist");
+ collections[collectionId].isActive = true;
+ collectionActivated[collectionId] = true;
+ }
// Or add automatic timeout:
+ uint256 constant ACTIVATION_DEADLINE = 30 days;
+ mapping(uint256 => uint256) public collectionCreatedAt;
function createMemorabiliaCollection(...) external onlyOrganizer {
// ...
+ collectionCreatedAt[collectionId] = block.timestamp;
}
function redeemMemorabilia(uint256 collectionId) external {
MemorabiliaCollection storage collection = collections[collectionId];
+ bool autoActive = block.timestamp >= collectionCreatedAt[collectionId] + ACTIVATION_DEADLINE;
+ require(collection.isActive || autoActive, "Collection not active");
// ...
}