The intended behavior of buySnow is to let a user pay either with native ETH or with WETH for the same Snow mint. The function picks a payment rail based on msg.value: if the user sent exactly s_buyFee * amount wei, the ETH path is used and no WETH is touched; otherwise, the WETH path is used and the ETH (if any) is silently retained by the contract.
The specific issue is that the branching logic uses an exact equality check (msg.value == s_buyFee * amount) instead of a threshold check. Any user who underpays ETH by even a single wei — because of a UI rounding error, a stale fee cache, or a manual miscalculation — falls through to the WETH branch. The WETH branch charges the full s_buyFee * amount in WETH via safeTransferFrom, but the misplaced ETH is now locked in the contract with no refund path. The only entity that can recover it is the fee collector via collectFee(). The user has paid twice (ETH + WETH) and received one mint.
Likelihood:
Reason 1: The condition msg.value == s_buyFee * amount is brittle. The user must compute s_buyFee * amount exactly off-chain and forward precisely that amount of wei. Any wallet that rounds the displayed fee, any UI that caches the fee for more than one block, or any manual calculation error — including being off by a single wei — routes the user into the WETH branch.
Reason 2: The contract does not advertise that the WETH branch can also receive ETH; the function signature buySnow(uint256 amount) payable and the parameter naming suggest the user is choosing one of two payment rails, not that imprecise ETH will be silently absorbed while WETH is also charged. Users who already hold WETH and approve the contract for the fee are especially prone to sending "almost enough" ETH and being double-charged.
Impact:
Impact 1: The user loses the entire msg.value of ETH (locked in the Snow contract, recoverable only by the fee collector via collectFee) AND the full s_buyFee * amount of WETH (transferred to the Snow contract as the actual fee). The user receives only one Snow mint in return — effectively paying twice for the same purchase.
Impact 2: The fee collector gains the stranded ETH, which creates a perverse incentive for the fee collector to discourage refunds of imprecise ETH payments. There is no on-chain mechanism for the user to recover their stranded ETH; recovery depends entirely on the off-chain goodwill of the collector role.
The fix (a) replaces exact-equality with >= so ETH overpayments route to the ETH path with an automatic refund of the surplus, and (b) explicitly refunds any ETH sent in the WETH path. Users can no longer be silently double-charged.
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.