An external call is made before the contract updates its own state. A malicious recipient contract re-enters the same function through its fallback/receive handler and repeats the withdrawal before the balance is zeroed, draining funds.
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "insufficient");
balances[msg.sender] -= amount; // EFFECTS before INTERACTION
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "transfer failed");
}
Incidents this entry cites by name:
Largest recorded losses in the same failure class — related, not the same bug:
The SaferICO scanner runs 201 detectors over your Solidity source, SAFE-0001 among them. Paste an address or the source itself — a small per-scan fee, shown before you sign, or unlimited on any plan.