Most of the high-severity findings in the audits we ship cluster around the same five patterns. None of them are new. All of them are still in production code we receive for review. If your team is preparing for an audit, scrub for these first.
1. Re-entrancy on non-obvious paths
Most teams now defend the obvious withdraw paths. The pitfall is in less-obvious external calls — token hooks, callback receivers, upgrade proxies — where state mutation happens after a call returns.
2. Oracle staleness with no upper bound
Heartbeat checks are common; max-age checks are not. Without an upper bound on staleness, your protocol can settle against price data minutes old in degraded conditions.
3. Access control via address rather than role
Hardcoding privileged addresses is fragile. Use OpenZeppelin AccessControl with roles and document who holds what role, when it can be transferred, and what the emergency revocation path is.
4. Insufficient invariants under fuzzing
Most audits now include fuzzing. The quality of fuzzing depends entirely on the invariants you write. Generic invariants find generic bugs. Protocol-specific invariants — that capture what your system promises users — find protocol-specific bugs.
5. Upgrade paths without rehearsal
Storage layouts, initializers, ownership transfers — every upgrade is an opportunity to lose money. Rehearse upgrades on a forked mainnet. Document the rollback path before deploying.