Snowman Merkle Airdrop

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

Fee collection is unbounded/untracked

Root + Impact

Description

buySnow() accepts msg.value in ETH even when the caller chooses the WETH payment branch (msg.value == 0 is not enforced, and excess ETH is never refunded).

collectFee() transfers address(this).balance to s_feeCollector without tracking s_feeCollected, draining any accidentally sent ETH alongside protocol fees.

function buySnow(uint256 amount) external payable canFarmSnow {
uint256 fee = calculateFee(amount);
if (msg.value == fee) {
// ETH payment path
} else {
@> // WETH payment path: msg.value is never checked to be 0 or refunded
i_weth.safeTransferFrom(msg.sender, address(this), fee);
}
_mint(msg.sender, amount);
}
​
function collectFee() external onlyOwner {
@> (bool success, ) = payable(s_feeCollector).call{value: address(this).balance}("");
require(success, "Fee collection failed");
}

Risk

Likelihood: Medium

Any user who accidentally attaches msg.value while paying via WETH (or overpays msg.value != fee) loses their ETH into the contract balance.

Impact: Medium

Unrefunded msg.value is permanently trapped from the user and swept to s_feeCollector on the next collectFee() call.

Proof of Concept

function test_WethPathTrapsMsgValue() public {
vm.deal(user, 1 ether);
vm.startPrank(user);
weth.approve(address(snow), fee);
// Caller accidentally sends 0.5 ether while paying via WETH
snow.buySnow{value: 0.5 ether}(amount);
vm.stopPrank();
​
// 0.5 ether is now trapped in Snow and drained by collectFee()
assertEq(address(snow).balance, 0.5 ether);
}

Recommended Mitigation

if (msg.value == fee) {
- // ETH payment path
+ s_feeCollected += fee;
} else {
+ if (msg.value > 0) revert Snow__UnexpectedEth();
i_weth.safeTransferFrom(msg.sender, address(this), fee);
}
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!