buySnow should accept either a precise ETH payment OR a WETH transfer, never both.
The branch decides on exact equality msg.value == s_buyFee * amount. Any other msg.value (including overpay or underpay) falls into the WETH branch, which still pulls the full WETH fee — and the original msg.value of ETH gets trapped in the contract balance.
Likelihood:
Reason 1: Frontends commonly add 1-wei rounding or gas-price-derived dust to msg.value — overpay routinely falls into the WETH branch.
Reason 2: Users commonly hold type(uint256).max WETH approvals, so the WETH transferFrom succeeds silently.
Impact:
Impact 1: User pays both WETH (fee) and ETH (trapped in the contract) for a single Snow purchase.
Impact 2: Underpayment with a WETH approval silently charges WETH instead of reverting, against user intent.
The PoC puts Alice in the most realistic possible state for this bug: she has ETH for the fee, she has WETH approved to the Snow contract at type(uint256).max (the default approval pattern most users have for fee tokens), and she calls buySnow with 1 wei too much ETH — a rounding artifact any frontend can produce. The contract's exact-match check fails, the else branch executes, and the WETH transferFrom silently drains her fee in WETH. The three post-state asserts together prove the double-charge: she received the Snow, her WETH is gone, and her ETH is now stuck in the contract balance with no withdrawal path back to her (only the collector can ever retrieve it via collectFee).
The fix turns the two-branch implicit fall-through into three explicit branches: exact ETH match (pay in ETH), zero msg.value (pay in WETH), or revert. This makes the user's intent unambiguous to the contract — there is no "ambiguous payment" path that silently charges the secondary rail. The revert on mismatch is preferable to a refund because (a) it surfaces frontend bugs immediately rather than masking them, and (b) refunds with .call introduce a reentrancy surface that is unnecessary if the exact-match invariant is enforced.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.