Snowman Merkle Airdrop

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

[L-02] `s_claimers` array declared but never populated — dead storage variable

Description

SnowmanAirdrop declares address[] private s_claimers on line 42 with a comment "array to store addresses of claimers", but claimSnowman() never pushes to this array. The variable wastes a storage slot at deployment and its existence suggests functionality that was intended but never implemented.

Vulnerability Details

// src/SnowmanAirdrop.sol, line 42
address[] private s_claimers; // array to store addresses of claimers
// @> NEVER written to in claimSnowman() or anywhere else
// src/SnowmanAirdrop.sol, lines 69-99
function claimSnowman(address receiver, bytes32[] calldata merkleProof, uint8 v, bytes32 r, bytes32 s)
external nonReentrant
{
// ... validation ...
i_snow.safeTransferFrom(receiver, address(this), amount);
s_hasClaimedSnowman[receiver] = true;
// @> s_claimers.push(receiver) is MISSING
emit SnowmanClaimedSuccessfully(receiver, amount);
i_snowman.mintSnowman(receiver, amount);
}

There is no getter function for s_claimers either, so even if it were populated, there would be no way to read it externally.

Risk

Likelihood:

  • The dead variable exists in every deployment.

Impact:

  • No direct security impact. Wasted storage slot and misleading code. If downstream logic (frontend, analytics) depends on querying the claimers list, it will always be empty.

Proof of Concept

function testExploit_ClaimersArrayEmpty() public {
// Complete a successful claim
bytes32 msgHash = airdrop.getMessageHash(claimer);
(uint8 v, bytes32 r, bytes32 s_sig) = vm.sign(claimerPrivateKey, msgHash);
airdrop.claimSnowman(claimer, proof, v, r, s_sig);
// Claim succeeded
assertEq(snowman.balanceOf(claimer), 1);
assertTrue(airdrop.getClaimStatus(claimer));
// But s_claimers is still empty — no getter exists, and no push was made
// The array serves no purpose in the contract
}

Recommendations

Either populate the array in claimSnowman() and add a getter, or remove it:

- address[] private s_claimers; // array to store addresses of claimers

If tracking claimers is needed, add:

s_claimers.push(receiver); // in claimSnowman()
function getClaimers() external view returns (address[] memory) {
return s_claimers;
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 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!