Root + Impact
Description
The buySnow function allows users to purchase Snow tokens using ETH or WETH during the farming period, minting tokens and updating the earn timer.
The function incorrectly handles payments: if the sent ETH (msg.value) does not exactly match the required fee (s_buyFee * amount),
it assumes WETH payment without verifying sufficient ETH or user intent, potentially causing ETH to be stuck or unintended WETH transfers.
Risk Likelihood:
Users send an incorrect msg.value (less or more than s_buyFee * amount) when attempting to pay with ETH.
Users have previously approved WETH for the contract, allowing automatic WETH transfers when ETH payment fails,
but the ETH will be also sent and it will be stuck in Snow contract.
Impact:
ETH sent with an incorrect amount becomes stuck in the contract, as it is not refunded or used.
Users may unintentionally pay with WETH, leading to unexpected token deductions or transaction failures if WETH balance or approval is insufficient.
Proof of Concept
function test_ETH_Stuck() public {
uint256 jerryBuys = 1e18;
vm.deal(jerry, 10e18);
vm.startPrank(jerry);
weth.approve(address(snow), FEE);
snow.buySnow{value: jerryBuys}(1);
vm.stopPrank();
assertEq(weth.balanceOf(address(snow)), FEE);
assertEq(snow.balanceOf(jerry), 1);
assertEq(jerry.balance, 10e18 - 1e18);
}
Result: 0.5 ETH is stuck in the contract, and WETH is also transferred to contract so user will end up paying ETH+wETH
Recommended Mitigation
function buySnow(uint256 amount) external payable canFarmSnow {
- // Check if the payment is made in ETH by comparing msg.value to the required fee (s_buyFee * amount).
- if (msg.value == (s_buyFee * amount)) {
- // Mint the specified amount of Snow tokens to the caller.
- _mint(msg.sender, amount);
- } else {
- // Assume payment in WETH; transfer the required fee (s_buyFee * amount) from the caller to the contract.
- i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
- // Mint the specified amount of Snow tokens to the caller.
- _mint(msg.sender, amount);
- }
+ uint256 requiredFee = s_buyFee * amount;
+ if (msg.value > 0) {
+ // Handle ETH payment
+ require(msg.value == requiredFee, "Incorrect ETH amount");
+ _mint(msg.sender, amount);
+ } else {
+ // Handle WETH payment
+ i_weth.safeTransferFrom(msg.sender, address(this), requiredFee);
+ _mint(msg.sender, amount);
+ }
// Update the global earn timer to the current block timestamp, resetting the one-week cooldown for earning free Snow tokens.
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}