Snowman Merkle Airdrop

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

Root + Impact

collectFee() uses unsafe bare i_weth.transfer() — return value is not checked

Description

  • The collectFee() function calls i_weth.transfer(s_collector, collection) using the bare ERC20 transfer(). The bare .transfer() call does not check the return value. ERC20 tokens that return false on failure (instead of reverting) will silently fail, and the function will continue as if the transfer succeeded.

// Snow.sol
function collectFee() external onlyCollector {
uint256 collection = i_weth.balanceOf(address(this));
@> i_weth.transfer(s_collector, collection); // Unchecked return value — can silently fail
(bool collected,) = payable(s_collector).call{value: address(this).balance}("");
require(collected, "Fee collection failed!!!");
}

Risk

Likelihood:

  • Any WETH implementation that returns false on failure triggers silent loss.

  • The contract imported SafeERC20 (using SafeERC20 for IERC20) but the developer used the raw .transfer() instead.

Impact:

  • WETH fees can be silently lost — the transaction succeeds but the collector receives nothing. The WETH remains stuck in the contract.

Proof of Concept

Explanation: The vulnerability is architectural. The contract globally imports SafeERC20, but calls .transfer() directly. Since transfer() does not revert on a false boolean return, failed WETH transfers will execute silently.

// This is the imported but unused safety wrapper:
// using SafeERC20 for IERC20; ← declared but not used for the transfer
// The fix is a one-line change:
// i_weth.transfer(s_collector, collection); ← unsafe
// i_weth.safeTransfer(s_collector, collection); ← safe

Recommended Mitigation

Explanation: The mitigation simply changes .transfer to .safeTransfer so that the execution reverts immediately if the ERC20 token transfer fails.

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

Lead Judging Commences

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