Root + Impact
When `msg.value` does not exactly equal `s_buyFee * amount`, `buySnow` falls into the WETH branch without reverting or refunding the ETH that was sent. The buyer pays the full price in WETH and loses the ETH, which `collectFee` later sweeps to the collector.
Description
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
Risk
Likelihood:
* The user sends a slightly wrong ETH amount (rounding, frontend bug) while holding a WETH approval to the contract.
Impact:
* The user loses all the ETH sent and is charged the full price again in WETH.
Proof of Concept
function test_M1_buySnowKeepsEthAndAlsoChargesWeth() public {
uint256 price = snow.s_buyFee() * 2;
address buyer = makeAddr("buyer");
MockWETH weth = MockWETH(address(uint160(uint256(vm.load(address(snow), bytes32(uint256(9)))))));
vm.deal(buyer, price);
weth.mint(buyer, price);
vm.startPrank(buyer);
weth.approve(address(snow), price);
snow.buySnow{value: price - 1}(2);
vm.stopPrank();
assertEq(snow.balanceOf(buyer), 2);
assertEq(buyer.balance, 1);
assertEq(weth.balanceOf(buyer), 0);
}
Recommended Mitigation
} else {
+ if (msg.value != 0) revert S__NotAllowed();
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));