@> function buySnow(uint256 amount) external payable {
@> if (block.timestamp > i_deploymentTimestamp + FARMING_DURATION) {
@> revert S__TimeLimit();
@> }
@> 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);
@> }
@> s_earnTimer = block.timestamp;
@> }
The README says Snow can be bought with either WETH or native ETH. The code decides which by exact equality: only msg.value == s_buyFee * amount takes the ETH path. Every other value, including a user who sends too much ETH, too little ETH, or ETH by mistake while intending to pay WETH, falls into the else branch, which pulls the full WETH price and mints, while the ETH that was sent stays in the contract (it can later be collected by the fee collector through collectFee()). There is no check that msg.value == 0 in the WETH branch, no refund of excess ETH, and no revert on a mismatched payment.
Impact: direct loss of funds for the buyer, who pays in both assets for one purchase. With the deployed fee of 5 ether per Snow (script/DeploySnow.s.sol), a buyer who sends 1 ether by mistake and has approved WETH loses 1 ether plus 5 WETH for 1 Snow. A buyer who sends 6 ether for 1 Snow loses 6 ether plus 5 WETH.
Explanation: only accept one payment method per call, and reject any other value instead of silently taking both.
if (msg.value == s_buyFee * amount) {
_mint(msg.sender, amount);
- } else {
+ } else if (msg.value == 0) {
i_weth.safeTransferFrom(msg.sender, address(this), s_buyFee * amount);
_mint(msg.sender, amount);
+ } else {
+ revert S__InvalidPayment();
}
Declare a new error S__InvalidPayment (or reuse an existing error) for the mismatched-payment case.