State shared across multiple functions is left inconsistent during an external call. The attacker re-enters through a DIFFERENT function that reads the not-yet-updated shared state, bypassing a guard that only protects the original function.
function withdraw() external nonReentrant {
uint256 bal = balances[msg.sender];
balances[msg.sender] = 0; // update before call
(bool ok,) = msg.sender.call{value: bal}("");
require(ok);
}
function transfer(address to, uint256 amt) external nonReentrant {
balances[msg.sender] -= amt;
balances[to] += 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-0002 among them. Paste an address or the source itself — a small per-scan fee, shown before you sign, or unlimited on any plan.