For educational purposes only; not investment or security advice. An audit report, tool result, resolved finding or matching source file is not certification, insurance, compensation or proof that a live deployment is safe.
Direct answer
A smart contract audit is a bounded examination of specified requirements, source code, build inputs, deployment logic and security assumptions during a stated period. Reviewers use complementary methods to identify defects, demonstrate exploit paths, evaluate impact and check proposed fixes. The conclusion applies only to the engagement snapshot and evidence described in the report.
The snapshot should pin the repository and commit or tree hash, submodules and dependency locks, compiler and settings, generated code, deployment scripts, target chains and addresses, proxy, implementation or beacon, constructor or initializer data, libraries, administrators, timelocks and a block or time. Explicit exclusions matter as much as inclusions: a frontend, keeper, oracle, bridge, governance process or off-chain signer can dominate risk while remaining outside the audit.
An audit needs a threat model and a specification. Identify assets, actors, privileged roles, trust boundaries, attacker capabilities, ordering and reorganization assumptions, external dependencies, state transitions and precise safety and liveness properties. An invariant without units, preconditions, quantifiers and exceptions can prove or test the wrong behavior perfectly.
Use four ledgers: scope, build and deployment identity; requirements, threats and invariants; findings, evidence and retest; and residual risk, acceptance and disclosure. A report with no critical finding is not a security certificate, a finding marked resolved is not necessarily deployed, and a proof covers only its encoded property, model and assumptions.
How it works
Manual review traces architecture, fund flow, cross-function state and economic intent. Static analysis finds patterns and data flows but can produce false positives and false negatives. Unit, integration, fork and differential tests compare concrete behavior. Stateful fuzz and invariant tests explore generated sequences, but results depend on handlers, selectors, seeds, corpus, runs, depth and the modeled environment.
Symbolic execution and formal verification can establish selected assertions under supported semantics and assumptions. A solver result of unknown, timeout or unsupported behavior is not a proof. Even a proved property can omit oracle economics, governance, deployment configuration, chain behavior or the requirement the team actually intended. Human review owns the specification and interpretation; an AI-generated observation is not a separate assurance method.
Each finding should identify the affected artifact and deployment, prerequisite, minimal proof, exploit path, reachability, privileges, attacker capital, repeatability, economic impact, severity rubric and recommendation. Exploitability or likelihood and impact are separate dimensions. A theoretical maximum, vulnerability name or tool label does not establish executable loss.
Statuses such as open, acknowledged, risk accepted, partially fixed, resolved and retested are not universal standards. A defensible closure binds the original issue to an exact fix commit, records the changed and adjacent paths tested, and states who retested what and when. Accepted risk remains risk, and a limited retest does not extend the original scope to all new code.
Upgradeable deployments require special reconciliation. Resolve proxy, implementation or beacon and admin slots; verify initializer and reinitializer behavior, implementation locking, storage compatibility, upgrade authorization, timelock or emergency bypass, migration and rollback. Reproduce creation and runtime bytecode from the audited build, then compare linked libraries, parameters, roles and initialized state on every chain.
The final report should state revision, auditors and dates, exact scope, methods and configurations, limitations, findings, evidence, remediation status, unresolved or accepted risks and disclosure terms. After release, monitor implementation hashes, roles, parameters, dependencies and incidents. Any material change creates a new delta requiring review; a badge attached to an old report does not follow future code automatically.
Use this workflow:
- Freeze the engagement manifest: repository, commit, dependencies, compiler and settings, generated and deployment code, chains, addresses, proxy stack, parameters, block, report revision, inclusions and exclusions.
- Define assets, actors, privileged roles, trust boundaries, attacker capabilities, lifecycle, ordering and liveness assumptions, external dependencies and measurable invariants.
- Reproduce the build and map architecture, storage, data, funds and control; reconcile source, artifacts, libraries, creation and runtime bytecode, initializer, roles and live deployment.
- Run complementary manual, static, unit, integration, fork, differential, fuzz, invariant, symbolic or formal methods with recorded tool versions, configurations, seeds, corpus, coverage, timeouts and unknowns.
- Record each issue’s affected artifact, prerequisite, proof, exploitability, impact, severity method, deployment exposure, recommendation and confidential evidence without treating a tool label as judgment.
- Freeze the fix commit and retest the issue, adjacent paths and invariants; validate proxy storage, initialization, migration, rollback, reproducible build and deployment receipts, then assign an evidence-backed status.
- Publish scope, methods, limits and residual risks; reconcile audited artifacts to every live chain and keep monitoring, disclosure, incident response and bug-bounty controls current as the system changes.
Examples
- A vault inflation path needs a complete ledger. An attacker deposits
1 assetthrough the first-deposit branch and receives1 share, then donates1,000,000 assets; totals become1,000,001 assetsand1 share. A victim deposits500,000 assets, and an unsafe floor calculation givesfloor(500,000 * 1 / 1,000,001) = 0 shares. If zero-share deposits are accepted, the vault holds1,500,001 assets; the attacker redeems them and gains500,000 assetsover its1,000,001-assetcontribution. If the implementation reverts on zero shares, this exact loss path does not execute. - File coverage is not deployment coverage. A manifest contains
24 source units,4 deployment scriptsand3 keeper services, or31 items. The engagement includes20 source unitsand2 scripts, so count coverage is22 / 31 = 70.96774194%; the other9 itemsare excluded. If the live proxy points to an implementation built from an excluded unit, coverage of that live implementation is0%despite the70.96774194%headline. - Fuzz observations are not an absence proof. A stateful run executes
2,000 sequences * 64 calls = 128,000 calls; an invariant fails in3 sequences, an observed3 / 2,000 = 0.15%of generated sequences. After a fix,10,000 sequences * 64 calls = 640,000 callsproduce zero failures. That is zero in this corpus, not proof; under an independence and stable-generator teaching assumption, the rule-of-three approximate95%upper bound is3 / 10,000 = 0.03%per generated sequence. - Finding closure and deployment identity are independent. A report has
12 findings:2 critical,3 high,4 mediumand3 low. Retest closes2 + 2 + 3 + 2 = 9, so count closure is9 / 12 = 75%; one high, one medium and one low remain. Audited runtime hash isH1, but the live implementation isH2, so deployment verification fails regardless of the closure rate. Replacing it with exactH1and matching proxy slot, initializer and roles proves identity only at the checked block.
Risks
- The repository, commit, submodule or generated source is stale or ambiguous.
- Compiler version, optimizer settings, libraries or dependencies are not pinned.
- Deployment scripts, constructor data, initializer or CREATE2 salt are excluded.
- The wrong chain, address, proxy, beacon or implementation is inspected.
- Source, artifact, creation bytecode and runtime bytecode do not reconcile.
- The threat model omits an actor, privilege, asset or trust boundary.
- The specification or invariant has wrong units, preconditions or exceptions.
- Admin, guardian, timelock, pause, upgrade or migration paths are missed.
- Oracle, token, bridge, keeper, governance or chain assumptions fail.
- Static analysis produces an untriaged false positive.
- Manual review, testing or fuzzing misses an ungenerated path.
- Fuzz harness, selector, seed, corpus, depth or state model is biased.
- Solver timeout, unsupported semantics or
unknownis mistaken for proof. - A correct proof formalizes the wrong requirement or incomplete system.
- Severity is anchored on a vulnerability name instead of exploitability and impact.
- Theoretical value at risk is mistaken for reachable loss or attacker profit.
- A fix introduces an adjacent regression or breaks an economic invariant.
- Proxy storage, initializer, upgrade or migration corrupts live state.
- An accepted, open or partially fixed issue is hidden by a passed-audit badge.
- The report is treated as insurance, certification, compensation or permanent coverage.
Common misconceptions
- “No critical findings means the contract is secure.” It describes reported findings within a bounded scope, time and method set, not all possible defects.
- “High test coverage or zero fuzz failures proves there are no bugs.” Those are measurements of selected code and generated paths, not absence proofs.
- “Formal verification proves the whole protocol safe.” It proves encoded properties of a model under assumptions; specifications and surrounding systems can still be wrong.
- “Resolved means every live deployment is fixed.” Closure requires independent retest plus build, bytecode, proxy, parameter and role reconciliation for each deployment.
- “A reputable auditor guarantees compensation or future upgrades.” Liability depends on the engagement contract, users may not be beneficiaries, and later code or configuration falls outside the old snapshot.
Related topics
Sources
- OWASP Smart Contract Security Verification Standard (SCSVS) - OWASP (accessed: 2026-08-13)
- Security Considerations - Solidity (accessed: 2026-08-13)
- Slither, the smart contract static analyzer - Crytic (accessed: 2026-08-13)
- Invariant Testing - Foundry (accessed: 2026-08-13)
- Certora User’s Guide - Certora (accessed: 2026-08-13)
- ERC-1967: Proxy Storage Slots - Ethereum Improvement Proposals (accessed: 2026-08-13)
- Writing Upgradeable Contracts - OpenZeppelin (accessed: 2026-08-13)
- Contract Metadata - Solidity (accessed: 2026-08-13)