Snowman Merkle Airdrop

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

`Snow::earnSnow` does not emit the declared `SnowEarned` event

Root + Impact

Description

  • The Snow contract declares a SnowEarned event at line 41, intended to be emitted when a user earns free Snow tokens.

  • The buySnow function correctly emits its corresponding SnowBought event after minting tokens.

  • However, the earnSnow function never emits the SnowEarned event after minting. The event is declared but never used anywhere in the contract.

// src/Snow.sol
event SnowEarned(address indexed earner, uint256 indexed amount); // @> declared but never emitted
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
// @> missing: emit SnowEarned(msg.sender, 1);
}

Risk

Likelihood:

  • Every single earnSnow call completes without emitting the event. This occurs 100% of the time during normal usage.

Impact:

  • Off-chain systems (event indexers, frontends, analytics dashboards) that listen for SnowEarned events will never receive data about free Snow token earnings.

  • There is an inconsistency between buySnow (which emits SnowBought) and earnSnow (which emits nothing), making on-chain activity harder to track and audit.


Proof of Concept

The SnowBought event is emitted in buySnow at line 89, but the analogous SnowEarned event is never emitted in earnSnow. A grep of the entire contract confirms zero occurrences of emit SnowEarned.

function testEarnSnowNoEvent() public {
address user = makeAddr("user");
// When user earns Snow, no SnowEarned event is emitted
vm.prank(user);
// vm.expectEmit would fail here because no event is emitted
snow.earnSnow();
// Compare with buySnow which correctly emits SnowBought
// This inconsistency breaks off-chain event tracking
}

Recommended Mitigation

Add the emit SnowEarned statement at the end of the earnSnow function, mirroring how buySnow emits SnowBought. This ensures consistent event emission across both token acquisition paths, allowing off-chain systems to accurately track all Snow token distributions.

function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
+ emit SnowEarned(msg.sender, 1);
}
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!