A signed message lacks a nonce, expiry, or domain binding, so the same signature can be submitted multiple times (replay) to repeat an action such as a withdrawal or permit.
mapping(address=>uint256) public nonces;
function claim(uint256 amt, uint256 nonce, uint256 deadline, bytes calldata sig) external {
require(block.timestamp <= deadline, "expired");
require(nonce == nonces[msg.sender]++, "bad nonce");
bytes32 d = _hashTypedDataV4(keccak256(abi.encode(TYPEHASH, msg.sender, amt, nonce, deadline)));
require(ECDSA.recover(d, sig) == signer, "bad sig");
payable(msg.sender).transfer(amt);
}
Largest recorded losses in the same failure class — related, not the same bug:
The SaferICO scanner runs 201 detectors over your Solidity source, SAFE-0044 among them. Paste an address or the source itself — a small per-scan fee, shown before you sign, or unlimited on any plan.