TL;DR
- I scanned ~653k commits across 5,583 crypto repos and found 2,851 security fixes that shipped to production first and got fixed later.
- The compiler ate the classic bugs: overflow collapsed after Solidity 0.8 made math safe by default, reentrancy is fading since everyone adopted the guard.
- What slips through now is semantic: logic and accounting errors that no tool can flag, because the code is "correct", it just does the wrong thing.
- The median bug sat live in production for 69 days before its fix. The quiet ones hide longest; the record is 4.7 years.
- For 4 real hacks the exact fix commit is in the set, verified against the post-mortems (SIR 2025, Stake DAO 2026, Belt and Siren 2021). The vulnerable code was live 35–247 days before the exploit, then patched within days of the money leaving.
- The takeaway: the tools closed the easy holes. What's left is a review problem, catch it on the PR or read about it in a post-mortem.
What got me started on this was one commit from SIR, a small DeFi trading protocol on Ethereum. Their code reused transient-storage slot 1. Transient storage is Ethereum's newish scratch space, a value you stash for the duration of a single transaction, and two different parts of SIR's code were told to use the same slot, so one silently overwrote the other. The buggy code went live at the end of January 2025. On March 30 an attacker called the contract pretending to be a Uniswap pool mid-swap, walked through the hole that overwrite opened, and left with about $355K $355k. The fix landed April 2, three days after the money was gone, in a commit whose message just reads "bugfix: slot 1 of transient storage was reused."
I wanted to know how many of those there are, and whether the kind of bug that slips through has changed. So I cloned ~5,600 repos (history only, no file contents) from DeFiLlama's protocol list (the standard registry of DeFi protocols) plus the top-starred contract repos on GitHub, scanned about 653k commits that touch contract code, and had an LLM read the actual diff of each candidate and judge: is this a real security fix, what kind of bug, how severe, and was the buggy code plausibly deployed when it got fixed. 2,851 came out looking like production bugs, meaning the vulnerable version shipped, ran with real money behind it, and got patched after the fact. The full dataset is a CSV, linked at the end.
One number jumped out before I'd even sorted anything. 2026 is already the biggest year in the set: 591 production bugs in six months, all but level with the 638 I found across the whole of 2025. Some of that is real, some is just that a fix from three months ago is far easier to mine than one from 2019 (recent commits are still on the default branch, the repo still exists). So I lean on shares, not raw counts, for anything that moves over time.
"Reached production" is a proxy: commit timing after the first release tag, plus the diff, not confirmed against every on-chain address. The set is whatever lives on public GitHub, so closed-source is invisible, and "is this a real bug" is an LLM's judgment, so any single row can be wrong, but general interesting insights still emerge
The easy bugs are fading
Quick vocabulary for the chart, one line each.
- Reentrancy: your contract calls out to someone else's code, and that code calls back in before you've updated your books (the bug behind the 2016 DAO hack).
- Overflow: arithmetic silently wrapping past its maximum.
- Logic errors: code that does exactly what it says and not what was meant.
- Accounting errors: the books themselves are wrong, shares or balances or fees that stop adding up under some sequence of actions.
- DoS / griefing: making a contract stop working without stealing anything.
Here's every fix I found, sorted into those classes, as a share of that year's total. Two lines fall, two climb, one sits flat and refuses to move.
Reentrancy fell from a fifth of all fixes in 2021 to about one in eight by 2025, and its raw count barely moved (78 fixes in 2025 vs 81 in 2023) while the dataset grew. The defense is a one-line guard (nonReentrant) that blocks the call-back-in trick, and at this point it's muscle memory. Logic errors are the flat line at the top: about a quarter of everything, every single year.
Where the overflow bugs went
The gray line is arithmetic overflow, and it's the one I'd tie to tooling. It was 22% of the fixes I found in 2019, though that's a small year (46 bugs), so don't read too much into the exact height. By 2021 it was down near 10% and it's been sliding since, to 4% now. The obvious structural reason is Solidity 0.8, which shipped in December 2020 and made arithmetic revert on overflow by default. Before that, every a + b was a potential SafeMath bug. After it, the compiler handles the common case. I can't cleanly isolate the exact quarter it kicked in from this data, but a whole category of bug getting smaller right as the compiler started catching it is about as good as causal evidence gets from git history.
Except overflow didn't die. There are still about 30 overflow fixes a year in the data, steady. People turn the checks back off inside unchecked { } blocks for gas, and downcasts like uint256 to uint128 still truncate silently with no revert. The compiler ate the obvious version and left the version that needs you paying attention. Tooling moved the bug, it didn't remove it.
What's left is what tools can't see
The two lines climbing are accounting errors and denial-of-service / griefing. Accounting went from a handful of fixes a year to over a hundred in the first half of 2026. None of these have a linter rule. No tool flags "you priced this LP token off raw balances that a flash loan can move" (a flash loan lets anyone borrow tens of millions with zero collateral, as long as it's repaid within the same transaction, which makes "an attacker with a huge balance" a free thing to be). That was the Belt Finance bug, fixed four days after someone took $6.3M off it. The shape of the tail now is semantic: the code reads fine until you think about it as an attacker with borrowed capital and a bad afternoon.
How long they hide
For the highest-value bugs I did the extra step: figure out not just when the bug was fixed, but when it was born. The trick is old software-engineering research (it's called SZZ): take the lines the fix deleted, and ask git blame who wrote them and when. That commit is, roughly, the one that introduced the bug. I ran this through GitHub's blame API for the hack-linked and high-severity subset, so for 661 bugs I know how long the bad code was actually live.
The mode is one to three months, with a fat tail. 89 bugs were live over a year; 29 over two. The record was a Celo logic error that sat in the tree for 4.7 years before anyone touched it.
The median bug that reaches production and later gets patched was live for a bit over two months. And that number has been flat since 2020. The tooling made the dumb bugs rarer, but it has done nothing for how long the subtle ones survive.
Two things make these numbers a floor, not a guess-in-the-middle. git blame points at the last commit that touched a line, so a refactor between birth and fix pulls the apparent birthday forward and shortens the dwell I measure. And a bug that's still hidden right now doesn't show up in this chart at all, because nobody has fixed it yet to give me an endpoint. Both of those cut the same direction. The real time-to-discovery is longer than what you see here, not shorter.
Then it gets interesting. Split the dwell time by class and a pattern falls out that I didn't expect.
The bugs that hide longest are the silent ones. Input-validation and access-control holes sit for months (medians 156 and 109 days) because nothing looks wrong until someone probes them. The fastest-fixed class is DoS / griefing (41 days): it breaks something you can see, a stuck function, a call that reverts. How long a bug hides tracks how loud its symptom is more than how severe it is.
The ones that became hacks
Matching a fix commit to a real exploit is harder than it sounds. Same protocol, same rough date, and a commit that clearly touches the exploited contract still usually isn't the actual remediation. I cross-referenced every fix against DeFiLlama's hack database and read each promising match against the public post-mortem by hand, and most fell apart. Out of 227 hacks in 2025 and 2026 alone, only a handful have their exact fix commit sitting in a public repo I could pin to the incident; the rest were patched privately, closed-source, or in a repo that never reveals which commit was the fix. Four survived verification end to end, and for those four the commit provably fixes what got exploited.
The vulnerable code was live between 35 and 247 days before someone exploited it. Then the asymmetry: three of the four were patched within two weeks of the money leaving, SIR in three days, Belt in four. Shipping the fix takes days. Finding out you need one takes months. (Stake DAO's 106 days is the hardened redeploy; the stolen funds were returned within days.)
As for how the bugs got there in the first place: of the 533 traced bugs where I can compare the introducing commit against the repo's first release, 72% were introduced after launch, in an upgrade or a feature commit, not on day one. The version that got audited is usually not the version that gets exploited. Siren is the counterexample: its reentrancy shipped at launch and sat for 247 days.
What I'd take from this
Seven years of tooling turned the 2018 bug list into a solved problem. The compiler reverts your overflow, the linter catches your missing guard, the template ships nonReentrant by default. What's left is everything a machine hasn't been taught to read: whether the accounting is right, whether an invariant survives borrowed capital, whether two features interact in a way nobody drew on the whiteboard. Those are the bugs that sit in production for months, take the money, and get patched in a commit that says "fix rounding."
So the tooling has to move up to meet them. Not another linter for the bugs the compiler already catches, but AI code review aimed squarely at the semantic class: a reviewer that reads every PR to production with your protocol's own logic in mind and asks the questions a good auditor would. That needs two things most teams don't have yet. First, documentation written for an agent instead of a new hire, spelling out the invariants, the value flows, and the specific ways this protocol can break. Second, that reviewer wired into every PR, not a point-in-time audit months before the code that actually ships. This dataset is full of bugs that were introduced after the audit or that the audit simply missed, then sat live for months. An audit is a snapshot; the exploited version is almost never the audited one. You want the check running continuously, on the diff, before it deploys.
I am working on a new article I will release soon where I try to benchmark how much a simple agent like a PR review agent can find of these bugs. Stay tuned for that :)
CSV file with data here



