Every serious deployment of on-chain code now passes through an audit, and almost every audit produces a document that the people relying on it do not know how to read. This guide is about reading it — what each part of a report is evidence of, and what it is not evidence of.
The framing that causes most of the damage is treating the report as a verdict. It is not a verdict. It is a record of a bounded search, and the boundaries are stated at the top.
What is a smart contract audit, precisely?
A defined set of files, at a defined commit, reviewed by named people over a defined period. Everything about that sentence matters. Change the commit and the findings no longer describe what is deployed. Change the file list and behaviour that was checked becomes behaviour that was not.
The output is a list of findings, each graded by severity, each with a status: resolved, partially resolved, acknowledged, or disputed. Alongside them sits a section usually titled security model and trust assumptions, and that section is where the actual answer lives.
Why is scope the line that matters most?
Because a review cannot find a problem in code it was not given. This sounds obvious written down and is routinely forgotten when a project says it has been audited, without saying what was.
Our reporting on this desk has returned to the pattern often enough to name it: audits find what the scope allows them to find. The failures that follow a clean report are usually not missed bugs. They are integrations added afterwards, upgrade paths outside the reviewed contracts, or an off-chain component nobody considered part of the system.
Three questions answer most of it. What commit was reviewed, and is that what is deployed? Which files were excluded, and why? Was anything added after the review closed?
How should severity counts be read?
Not as a score. Severity describes the impact of a finding if exploited, combined with how reachable it is — it does not describe the quality of the code, and comparing counts between reports compares two different scopes reviewed by two different teams.
Three reviews this desk has covered, put side by side, show why the totals mislead:
| Review | Total | Critical | High | What the report was actually about |
|---|---|---|---|---|
| Miden contract library | 60 | 0 | 0 | Authorisation: policy activation, role assignment, what a signature binds |
| A nineteen-finding review | 19 | 0 | — | Six findings still open at publication |
| A two-file review | 11 | 0 | — | Five trust assumptions stated alongside the findings |
The Miden review is the clearest case. Sixty findings, none critical and none high — a result the client wanted and earned. But the medium band was dominated by authorisation rather than arithmetic: transfer policies registered as reserved that could never be activated, ownership transfers permanently bound to first-version account identifiers, authority-gated setters that turned out to be permissionless in one component pairing.
None of those is a critical finding. All of them are the shape of problem that becomes an incident when somebody builds a bridge on top.
What are trust assumptions, and why do they matter more than findings?
A trust assumption is something the reviewers agreed to treat as given: that an owner key is honest, that an oracle reports truthfully, that an upgrade will be governed. Assumptions are not findings, so they do not appear in the counts, and they are frequently where the real risk sits.
A report listing eleven findings and five assumptions is telling you two things. The first is what was wrong. The second is the list of conditions under which the first is complete — and if any of the five stops holding in production, the report stops describing the deployed system.
The practical test: if the assumptions section were printed alone, with no findings, would you still deploy? If the answer depends on facts you cannot verify, the audit did not resolve your risk; it documented it.
Which classes of bug are actually showing up?
The distribution has moved, and it has moved away from the categories that dominate older guidance.
- Authorisation and access control. Who may call what, under which component pairing, and whether a guard that reads as protective is enforced at the boundary it appears to protect.
- What a signature commits to. Signed summaries that bind neither an expiration nor a reference block, and state reads that reflect a prover-chosen block rather than the current one.
- Reentrancy, returned through token callbacks. The classic pattern did not disappear; it moved into hooks that the calling contract did not model as external calls.
- Controls placed at the interface rather than in the logic. A restriction enforced in a front end, or in one entry point of several, is documentation rather than a control.
- Circuit bugs, where the system uses proofs. A constraint that is missing is not a bug that reverts — it is a bug that accepts.
How do audits, formal verification and bug bounties differ?
They answer different questions and are not substitutes, though they are frequently priced as though they were.
| Audit | Formal verification | Bug bounty | |
|---|---|---|---|
| Answers | Did trained reviewers find problems in this scope? | Does the code satisfy this stated property? | Will anyone find something once it is live? |
| Coverage | Whatever the reviewers reached in the time | Exactly the properties specified, completely | Whatever attracts a researcher |
| Silent failure mode | Scope excluded the problem | The property was the wrong property | The exploit is worth more than the bounty |
| Timing | Before deployment | Before deployment | After deployment, indefinitely |
Formal verification has become materially more usable in the past two years, and it changes what an audit is for: once a property is proved, reviewer time moves to the parts no specification covers. It does not remove the need for review, because writing the wrong property is easy and proving it is no defence.
Bug bounties carry a structural weakness this desk has reported repeatedly: they still price below the exploit. A programme offering less than the value at risk is asking a researcher to accept a discount for honesty, and the economics of that are not on the defender's side.
How to read a report in ten minutes
- Start at the scope. Commit hash, file list, dates. Compare the commit to what is deployed now.
- Read the trust assumptions section next, before any finding. It defines what the rest of the document means.
- Look at unresolved and partially resolved findings, and at the client's stated reason for each. A declined medium finding with a good reason is fine; a declined one with no reason is a decision nobody wrote down.
- Read the medium band, not the critical band. Zero criticals is the expected result; the mediums tell you where the system is actually delicate.
- Check whether anything in the report describes off-chain components, upgrade authority or key management — and if not, note that those were not reviewed by anyone.
What an audit is worth, honestly
It is worth a great deal and it is not what most readers think it is. A competent review by people with no stake in the answer catches classes of error that authors cannot see in their own work, and it produces a written record of what was assumed — which is valuable even when no finding is severe.
What it cannot do is convert uncertainty into safety. The report describes a search that ended on a date, in a file list, under stated assumptions. Everything outside those three boundaries is unexamined, and that is where the incidents keep happening.
Questions
- Does a smart contract audit guarantee the contract is safe?
- No. An audit is a time-boxed review of a defined file list at a defined commit, under stated trust assumptions. It reports what trained reviewers found inside those boundaries. It cannot speak to code that was out of scope, to changes made after the review, or to assumptions that stop holding in production.
- What does zero critical findings mean?
- That the obvious high-impact classes were absent from the reviewed scope. It is the normal outcome of a competent review of competent code, not a distinction. The informative part of a clean report is the medium band and the trust assumptions, not the critical count.
- Why do audited protocols still get exploited?
- Most often because the failure was outside scope — an integration added afterwards, an upgrade path in a contract that was not reviewed, an off-chain component, or a key management practice. The report described a bounded search, and the incident happened outside the boundary.
- Is formal verification better than an audit?
- It answers a different question. Verification proves that code satisfies a stated property, completely, and an audit asks whether trained reviewers can find problems at all. Verification's failure mode is specifying the wrong property, which no amount of proving detects. In practice they are complements, and verification frees reviewer time for the parts no specification covers.
- How much of a report should I actually read?
- The scope, the trust assumptions, and every finding that was not resolved. That is usually two or three pages and contains most of the decision-relevant content. The resolved findings are a record of work already done.
- Do bug bounties replace an audit?
- No, and the economics are the reason. A bounty pays after deployment and competes with the value of the exploit itself; programmes that price below the amount at risk are asking researchers to accept a discount for disclosing. A bounty is a useful addition to a review, not a substitute for one.