uint256 private s_earnTimer;
function earnSnow() external canFarmSnow {
@> if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
import {DeploySnow} from "../script/DeploySnow.s.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
* @title PoC_Snow_GlobalTimerDoS
* @notice Shows that `earnSnow()` uses a SINGLE global timestamp (`s_earnTimer`),
* letting the first caller block everyone else from earning for an entire
* week, a denial-of-service bug.
*/
contract PoC_Snow_GlobalTimerDoS is Test {
Snow snow;
MockWETH weth;
address alice;
address bob;
function setUp() public {
DeploySnow deployer = new DeploySnow();
snow = deployer.run();
weth = deployer.weth();
alice = makeAddr("alice");
bob = makeAddr("bob");
}
function testFirstEarnerBlocksOthers() public {
vm.prank(alice);
snow.earnSnow();
assertEq(snow.balanceOf(alice), 1);
vm.prank(bob);
vm.expectRevert(Snow.S__Timer.selector);
snow.earnSnow();
vm.warp(block.timestamp + 1 weeks);
vm.prank(bob);
snow.earnSnow();
assertEq(snow.balanceOf(bob), 1);
}
}
- uint256 private s_earnTimer;
+ mapping(address => uint256) private lastEarn;
- if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
+ if (block.timestamp < lastEarn[msg.sender] + 1 weeks) {
revert S__Timer();
}
_mint(msg.sender, 1);
- s_earnTimer = block.timestamp;
+ lastEarn[msg.sender] = block.timestamp;