Root + Impact
Description
The collection.currentItemId
Start at 1,but this was not taken into account when calculating the total amount.
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,
isActive: activateNow
});
emit CollectionCreated(collectionId, name, maxSupply);
return collectionId;
}
In the following code, collection.currentItemId
is assumed to start counting from 0.
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");
Risk
Likelihood: High
This feature is widely used.
Impact: Medium
The total amount that can be redeemed is one less than expected.
Proof of Concept
function test_RedeemMemorabilia_Only_One() public {
vm.prank(user1);
festivalPass.buyPass{value: VIP_PRICE}(2);
vm.prank(organizer);
uint256 perfId = festivalPass.createPerformance(block.timestamp + 1 hours, 2 hours, 250e18);
vm.warp(block.timestamp + 90 minutes);
vm.prank(user1);
festivalPass.attendPerformance(perfId);
vm.prank(organizer);
uint256 collectionId = festivalPass.createMemorabiliaCollection(
"Festival Poster",
"ipfs://QmPosters",
100e18,
1,
true
);
vm.prank(user1);
vm.expectRevert("Collection sold out");
festivalPass.redeemMemorabilia(collectionId);
}
Recommended Mitigation
// Redeem a memorabilia NFT from a collection
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+1, "Collection sold out");