Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Missing and Poorly Indexed Events

Missing and Poorly Indexed Events

Description

  • Missing Events: State-mutating functions should emit events for proper off-chain tracking. The FestivalPass::setOrganizer, FestivalPass::configurePass, and BeatToken::setFestivalContract functions are missing event emissions. Additionally, the FestivalPass::withdraw function fails to emit the FundsWithdrawn event, despite it being declared in the IFestivalPass interface.

  • Poor Indexing: The FestivalPass::MemorabiliaRedeemed event lacks an indexed collectionId parameter, which makes filtering and querying for memorabilia redemptions by collection inefficient for off-chain tools.

// 1. Missing Events
function setOrganizer(address _organizer) public onlyOwner {
organizer = _organizer;
}
function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
passSupply[passId] = 0; // Reset current supply
}
// Organizer withdraws ETH
function withdraw(address target) external onlyOwner {
payable(target).transfer(address(this).balance);
}
// 2. Poor Indexing
event MemorabiliaRedeemed(address indexed collector, uint256 indexed tokenId, uint256 collectionId, uint256 itemId);

Risk

Likelihood:

  • Off-chain actors rely on events to track state changes and query specific data, such as memorabilia redemptions within a collection.

Impact:

  • Front-ends and analytics tools will need to scan full historical logs to track these state changes, significantly increasing RPC costs and potentially leading to missed or inaccurate state transitions.

Proof of Concept

The following test demonstrates that the withdraw function does not emit the FundsWithdrawn event, causing the test to fail.

event FundsWithdrawn(address indexed to, uint256 amount);
function test_Audit_WithdrawMissingEvent() public {
vm.prank(user1);
festivalPass.buyPass{value: GENERAL_PRICE}(1);
vm.startPrank(owner);
vm.expectEmit(true, false, false, true, address(festivalPass));
emit FundsWithdrawn(organizer, address(festivalPass).balance);
festivalPass.withdraw(organizer);
}

Recommended Mitigation

  1. Define the missing events in IFestivalPass.sol and add their emission in FestivalPass.sol. For BeatToken, define and emit the event directly within the contract.

  2. Add indexing for the collectionId parameter in the MemorabiliaRedeemed event.

// 1. Add missing events
+ event OrganizerUpdated(address indexed organizer);
+ event PassConfigured(uint256 indexed passId, uint256 price, uint256 maxSupply);
function setOrganizer(address _organizer) public onlyOwner {
organizer = _organizer;
+ emit OrganizerUpdated(_organizer);
}
function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
passSupply[passId] = 0; // Reset current supply
+ emit PassConfigured(passId, price, maxSupply);
}
function withdraw(address target) external onlyOwner {
+ uint256 amount = address(this).balance;
- payable(target).transfer(address(this).balance);
+ payable(target).transfer(amount);
+ emit FundsWithdrawn(target, amount); // emit event defined in the interface
}
// BeatToken.sol
+ event FestivalContractUpdated(address indexed festival);
function setFestivalContract(address _festival) external onlyOwner {
require(festivalContract == address(0), "Festival contract already set"); //@audit cannot be reused for other festivals
festivalContract = _festival;
+ emit FestivalContractUpdated(_festival);
}
// 2. Add indexing in IFestivalPass.sol
- event MemorabiliaRedeemed(address indexed collector, uint256 indexed tokenId, uint256 collectionId, uint256 itemId);
+ event MemorabiliaRedeemed(address indexed collector, uint256 indexed tokenId, uint256 indexed collectionId, uint256 itemId);
Updates

Lead Judging Commences

inallhonesty Lead Judge 28 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Missing events / Events not properly configured

Informational. This protocol doesn't rely on events to function, they are just nice to have, but not mandatory.

Support

FAQs

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