Snowman Merkle Airdrop

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

Stuck Tokens in Airdrop Contract

Root + Impact

Description

  • The SnowmanAirdrop contract is intended to facilitate the claiming of Snowman NFTs by users who stake their Snow tokens, transferring those tokens from the user to the airdrop contract itself as part of the claim process. This effectively acts as a burning or staking mechanism where the tokens are held by the contract to represent the exchange for NFTs.

    However, once the Snow tokens are transferred to the SnowmanAirdrop contract via the safeTransferFrom call in claimSnowman, they become permanently locked because there is no implemented function to withdraw, recover, or redistribute these accumulated tokens. This design flaw means the contract serves as an unintended token sink, with no administrative or owner-controlled mechanism to access the funds, potentially leading to significant value being trapped if the protocol gains traction and many users claim NFTs.

@> function claimSnowman(address receiver, bytes32[] calldata merkleProof, uint8 v, bytes32 r, bytes32 s)
@> external
@> nonReentrant
@> {
@> // ...
@> i_snow.safeTransferFrom(receiver, address(this), amount); // send tokens to contract... akin to burning
@> // ... No withdrawal function
@> }

Risk

Likelihood:

  • This vulnerability is unlikely to be exploited maliciously since it doesn't involve direct theft or attack vectors, but it arises naturally from normal protocol usage as more claims occur over time, especially if the airdrop sees high participation.

Impact:

  • The protocol will accumulate Snow tokens indefinitely without any utility or means of retrieval, which could lock away substantial value in a live deployment if adoption is high, leading to lost funds for the project team or community; additionally, there's no way to recover from errors like accidental transfers or to repurpose the tokens at the end of the airdrop period.

Proof of Concept

To demonstrate this issue, assume the contract has been deployed and multiple users have successfully claimed Snowman NFTs by staking their Snow tokens. The following steps illustrate how the tokens become irretrievable:
Users call claimSnowman, which executes the transfer:
@> i_snow.safeTransferFrom(receiver, address(this), amount);
After several claims, query the Snow token balance of the airdrop contract:
@> snow.balanceOf(airdropAddress); // This will return the total accumulated (staked) amount, confirming tokens are held by the contract.
Attempt to retrieve the tokens: There is no function (e.g., withdrawSnow) available for the owner or anyone else to call, so the balance remains stuck permanently, with no way to transfer it out even if the owner intends to.
This PoC highlights that while the transfer succeeds as designed, the absence of a recovery mechanism makes the tokens inaccessible, simulating a real-world scenario where value is lost post-claims.

Recommended Mitigation

To prevent tokens from being permanently stuck, introduce a secure withdrawal function accessible only by the contract owner, allowing recovery of accumulated Snow tokens without compromising the airdrop's integrity. This could be used for error correction, protocol upgrades, or redistributing funds at the end of the farming period.
// No changes to existing transfer logic
// Add onlyOwner withdrawal function
+ function withdrawSnow(uint256 amount) external onlyOwner {
+ i_snow.safeTransfer(owner(), amount);
+ }
Updates

Lead Judging Commences

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