Snowman Merkle Airdrop

AI First Flight #10
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: medium
Invalid

Missing FeeCollected Event Emission Reduces Protocol Transparency



Description

The collectFee() function in the Snow contract withdraws accumulated WETH and ETH fees to the designated collector address. As a standard practice for external state-changing functions, it should emit an event to enable off-chain monitoring, indexing, and auditability.

However, the function does not emit the declared FeeCollected() event, breaking consistency with other protocol events and reducing transparency for users, indexers, and monitoring systems.

https://github.com/CodeHawks-Contests/2025-06-snowman-merkle-airdrop/blob/b63f391444e69240f176a14a577c78cb85e4cf71/src/Snow.sol#L107

Risk

Likelihood:

  • Reason 1: The collectFee() function is intended to be called regularly during protocol operation, making the missing event omission consistently present.

  • Reason 2: Observability tools (e.g., The Graph, block explorers, dashboards) rely on events to track administrative actions—this gap will always affect external visibility.

Impact:

  • Impact 1: Users and governance cannot reliably verify when or how often fee withdrawals occur without parsing raw transaction calldata or balance diffs.

  • Impact 2: The inconsistency undermines auditability and deviates from Ethereum best practices, where all significant state changes are event-logged (e.g., as in ERC20, OpenZeppelin patterns).

Proof of Concept

Test to confirm event is NOT emitted
function testCollectFeeDoesNotEmitEvent() public {
Setup: buyer purchases Snow with WETH
address buyer = makeAddr("buyer");
weth.mint(buyer, 1e18);
vm.startPrank(buyer);
weth.approve(address(snow), 1e16);
snow.buySnow(1);
vm.stopPrank();
Collector calls collectFee
vm.expectEmit(false, false, false, true);
emit snow.FeeCollected(); / This expectation will FAIL
vm.prank(collector);
snow.collectFee(); / No event is emittedtest fails
}

Recommended Mitigation

function collectFee() external onlyCollector {
uint256 collection = i_weth.balanceOf(address(this));
i_weth.transfer(s_collector, collection);
(bool collected,) = payable(s_collector).call{value: address(this).balance}("");
require(collected, "Fee collection failed!!!");
emit FeeCollected(); Fixed
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!