Beatland Festival

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

FestivalPass.sol - Withdraw Function Fails to Emit Required Interface Event

Description

The FestivalPass contract's withdraw function fails to emit the FundsWithdrawn event that is declared in the IFestivalPass interface, breaking event-based integrations and monitoring systems. The interface declares a FundsWithdrawn event that should be emitted when ETH is withdrawn from the contract, but the implementation's withdraw function transfers funds successfully without emitting this event, creating a mismatch between the interface specification and actual behavior.

Root Cause

The interface declares an event that the implementation never emits:

  1. Missing Event Emission: The withdraw function should emit FundsWithdrawn event per interface specification but doesn't

  2. Access Control Documentation Mismatch: Interface documentation states "organizer withdraws" but implementation uses onlyOwner

// Interface declares this event
event FundsWithdrawn(address indexed organizer, uint256 amount);
// But implementation doesn't emit it
function withdraw(address target) external onlyOwner {
payable(target).transfer(address(this).balance);
// Missing: emit FundsWithdrawn(target, address(this).balance);
}

Risk

Likelihood: High - The issue occurs every time the withdraw function is called, as the event is never emitted in the implementation.

Impact: Low - No funds are at risk, but external integrations relying on the interface specification will fail to receive expected events.

Impact

  • Event monitoring systems and indexers will miss withdrawal events

  • Audit logs and compliance tracking will be incomplete

  • Frontend applications listening for withdrawal events won't be notified

Proof of Concept

The interface declares that withdraw should emit FundsWithdrawn but the implementation doesn't emit it

contract InterfaceComplianceTest is Test {
FestivalPass festivalPass;
BeatToken beatToken;
address owner = address(this);
address organizer = address(0x1);
// Events @audit - This event is declared in interface but never emitted
event FundsWithdrawn(address indexed organizer, uint256 amount);
receive() external payable {}
function setUp() public {
beatToken = new BeatToken();
festivalPass = new FestivalPass(address(beatToken), organizer);
// Fund the contract by having someone buy a pass
vm.prank(organizer);
festivalPass.configurePass(1, 1 ether, 10);
address buyer = address(0x123);
vm.deal(buyer, 1 ether);
vm.prank(buyer);
festivalPass.buyPass{value: 1 ether}(1);
}
function test_MissingEventEmission() public {
// Record logs before the call
vm.recordLogs();
// Call withdraw - it transfers funds but doesn't emit the event
festivalPass.withdraw(owner);
// Get all emitted logs
Vm.Log[] memory logs = vm.getRecordedLogs();
// Verify that no FundsWithdrawn event was emitted
bool fundWithdrawnEmitted = false;
for (uint i = 0; i < logs.length; i++) {
// FundsWithdrawn event signature: keccak256("FundsWithdrawn(address,uint256)")
if (logs[i].topics[0] == keccak256("FundsWithdrawn(address,uint256)")) {
fundWithdrawnEmitted = true;
break;
}
}
// Assert that the event was NOT emitted
assertFalse(fundWithdrawnEmitted, "FundsWithdrawn event should not be emitted");
}
}

Recommended Mitigation

Fix the implementation to emit the event as declared in the interface:

function withdraw(address target) external onlyOwner {
payable(target).transfer(amount);
+ emit FundsWithdrawn(target, address(this).balance);
}

This ensures that external systems relying on the interface specification will receive the expected withdrawal events for proper monitoring and integration.

Updates

Lead Judging Commences

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