buySnow supports two payment methods: pay the exact price in native ETH, or pay in WETH.
The branch distinguishing them tests only for the exact ETH price. Any other msg.value — including a near miss — falls into the else branch, which pulls the full price in WETH and mints. The ETH attached to that same call is never refunded and never credited; it simply stays in the contract. The buyer pays twice and receives the normal amount.
Likelihood:
Every time a buyer attaches a msg.value that is not exactly s_buyFee * amount. The function is payable and silently accepts any value, so there is no guard rail.
The price is a 1e18-scaled product (s_buyFee = _buyFee * PRECISION, so 5 ETH per unit at the deploy script's FEE = 5), which makes an off-by-a-factor miscalculation a routine integration and front-end error rather than an exotic one.
It also triggers on the reverse mistake: a user intending to pay in WETH who leaves a non-zero msg.value in the transaction loses it.
Impact:
Direct, unrecoverable loss of the buyer's ETH. There is no refund path and no owner-callable rescue — the only way the ETH ever leaves is through collectFee, which sends it to the fee collector.
The buyer pays roughly double for the same quantity of Snow, with no error and no event distinguishing the two payment paths.
Paste the harness and test function(s) into test/PoC.t.sol, placing test functions inside contract PoC. Any additional helper contracts go outside contract PoC. Run forge test --match-path test/PoC.t.sol -vv.
The buyer sends 1 ETH toward a 2-ETH purchase and ends down 1 ETH and 2 WETH, holding only the ordinary amount of Snow.
Treat a non-zero msg.value as an explicit intent to pay in ETH and reject it when it is not exact, rather than falling through to the WETH path.
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.