Smart contract audit checklist: how to prepare and what comes after
What to finish before auditors start, what to send them, a self-review checklist by bug class, and the post-audit work that keeps an audited protocol from becoming a post-mortem.

A smart contract audit checklist has three parts: what you finish before the auditors start (frozen code, documentation, tests, static analysis), what you hand them (scope, threat model, build instructions, known issues), and what you do after the report (fix review, publication, bug bounty, monitoring). Teams that do the first part well get more out of the same audit budget, because auditors spend their days on your logic instead of on setup and questions. Teams that skip the third part are the ones who end up writing post-mortems for audited code.
This post is the checklist we use when we prepare client code for external review. For what audits cost and how that compares to losses, see our post on smart contract security ROI.
Why preparation matters more than the auditor's brand
An audit is a time-boxed review by a few people. Every hour they spend working out how to compile your repo, what a function is supposed to do, or which contracts are in scope is an hour not spent finding bugs. OpenZeppelin's readiness guide says a protocol should reach the point where the code is already tested, documented, and ready for deployment before it's audited.
It's also worth being honest about what an audit is. It lowers risk. It doesn't remove it. Audius's governance contracts had been reviewed by two firms, and neither caught the storage collision that let an attacker take over governance in 2022. Euler's vulnerable donateToReserves function was, per the team, reviewed during an outside audit, and the 2023 exploit still cost about $197 million. Your preparation and your post-launch process carry a lot of the load.
Timing: when to book and when to freeze
- Book early. Reputable firms are often booked weeks or months out. Reserve a slot as soon as your architecture is stable, then work backwards.
- Audit when the code is done. OpenZeppelin's guide warns that if the audit happens too soon, further changes negate its value, and if it happens after deployment, the options for fixing vulnerabilities are much more limited.
- Freeze a commit. Pick a commit hash on a dedicated branch and tell the auditors that's the scope. Trail of Bits' review checklist asks you to freeze a commit hash, branch or release, because "working on a moving target makes the review more difficult."
- Plan the fix window. Leave one to three weeks after the report for fixes and the fix review before mainnet. That's our planning estimate, and teams that skip it end up launching with half-reviewed fixes.
What auditors need from you
| Artifact | Why it matters | What good looks like |
|---|---|---|
| Scope list | Prevents wasted time and gaps | Exact files and contracts, line counts, commit hash, what's out of scope and why |
| Build and test instructions | Day one shouldn't be spent on setup | forge build and forge test work from a fresh clone; pinned compiler and dependency versions |
| Architecture overview | Auditors need the system model before the code | A diagram of contracts, external calls, token flows and off-chain components |
| Specification | Bugs are deviations from intended behavior | What each external function should do, who may call it, and the expected state changes |
| Invariants | Gives auditors something to break | Plain statements like "total shares equal the sum of user shares" or "a healthy position can't be liquidated" |
| Roles and privileges | Admin powers are a major bug class | Every privileged role, what it can do, who holds it (multisig, timelock, EOA) |
| External dependencies | Integrations fail in ways your tests don't model | Oracles, tokens with odd behavior, bridges, other protocols, and the assumptions you make about each |
| Known issues and previous reports | Avoids paying for duplicate findings | Accepted risks, earlier audit reports and what changed since |
| Deployment plan | Many bugs live in deployment and config | Deployment scripts, constructor and initializer parameters, chain list, upgrade setup |
Trail of Bits' checklist also asks teams to set a goal for the review: your biggest concerns, the complex and fragile components, and your worst-case scenario. Do this. A paragraph saying "we're least sure about the liquidation math and the oracle fallback" focuses the team better than any amount of general documentation.
Internal checklist before you hand over code
Code hygiene
- Remove dead code, unused imports, commented-out functions and leftover test helpers from in-scope files.
- Pin the Solidity compiler version. Floating pragmas in deployable contracts make the audited bytecode ambiguous.
- Use maintained libraries (OpenZeppelin, Solady, Solmate) at pinned versions rather than copied snippets.
- Add NatSpec to every external and public function: what it does, who may call it, what it reverts on.
- Resolve every
TODO. An auditor who finds one will reasonably ask whether the code is finished. - Make naming consistent. If the spec says "shares", the code shouldn't say "units" in one contract and "claims" in another.
Tests
- Unit tests for every external function, including failure paths and access control.
- Integration tests against forked mainnet for every external protocol you call.
- Fuzz tests on math-heavy functions: share calculations, interest, fees, rounding.
- Invariant tests for your core properties. Foundry runs functions prefixed with
invariantagainst random call sequences and checks the property after each call. If you can't write an invariant for a contract, you probably can't explain its intended behavior yet either. - Coverage report attached. High line coverage alone proves little, but low coverage tells the auditor where to look first.
Static analysis and tooling
- Run Slither and triage every finding. Mark false positives with a reason, so auditors don't re-report them.
- Run your language's linters (Solhint for Solidity; Clippy for Rust programs).
- Check storage layout if anything is upgradeable. Our post on upgradeable smart contracts covers the OpenZeppelin validator and the initializer bugs to rule out.
- Check contract sizes against the 24 KB limit before the audit, not after. Splitting a contract late means re-auditing it.
Self-review by bug class
Before external review, have someone on your team who didn't write the code go through it with this list. Most expensive findings fall into a few buckets.
- Access control. Every state-changing function: who can call it, and is that enforced? Are initializers protected? Can a role grant itself more power?
- External calls and reentrancy. Do you update state before calling out? Do callbacks from ERC-777, ERC-1155 or hook-based tokens reach functions that assume they can't be re-entered? Cross-function and read-only reentrancy count too.
- Oracle and price manipulation. Does any price come from a spot pool balance that a flash loan can move within one transaction? Are stale or zero oracle answers rejected?
- Rounding and precision. Does rounding always favor the protocol? Can the first depositor in an empty vault inflate the share price against the next one?
- Token assumptions. Fee-on-transfer, rebasing, tokens that return no boolean, tokens with blocklists, tokens with 6 or 24 decimals.
- Signatures. Is there replay protection across chains, contracts and nonces? Is
ecrecoverreturningaddress(0)handled? - Governance and flash-loaned voting power. Voting power must come from a past snapshot. Beanstalk lost about $182 million when an attacker flash-borrowed enough tokens to control 79% of votes in a single transaction.
- New code paths in old systems. The Euler exploit came from a function added in a later governance proposal that skipped the liquidity check other transfer paths ran. Treat every added function as a new entry point and check it against all existing invariants.
- Denial of service. Unbounded loops over user-controlled arrays, and external calls that can revert and block a whole batch.
- Admin and upgrade powers. List what a compromised admin key could do. If the answer is "drain everything", that's a finding even if the code is correct.
Choosing the type of review
There are a few formats, and they combine well.
| Format | Strength | Weakness | Fits |
|---|---|---|---|
| Private audit firm | Senior reviewers, a direct relationship, scheduled fix review | Few reviewers, fixed time box | Every protocol that will hold meaningful value |
| Competitive audit contest | Many reviewers, broad coverage of edge cases | Variable quality, triage overhead, findings can be noisy | Larger codebases after a private audit, or as a second opinion |
| Solo or small-team reviewer | Fast, cheaper, often very good on specific domains | Depth depends entirely on the person | Small changes, diff reviews, early design reviews |
| Formal verification | Proves specific properties hold for all inputs | Only proves what you specify; specs take real effort | Core math, invariants in lending and AMMs |
For a typical DeFi launch our planning default is one private audit, then a contest or second private review if the TVL target is high, then a bug bounty from day one. For a simple token or vesting contract, a single focused review is usually enough.
During the audit
- Hold a kickoff call. Walk through the architecture and your review goals in the first hour.
- Keep one engineer on call to answer questions the same day. Slow answers burn audit days.
- Don't push changes to the frozen commit. If you must fix something urgent, tell the auditors and agree whether it's in scope.
- Ask for findings as they come, through a shared channel, so you can start on critical fixes early.
After the audit: the part teams skip
Fix, then get the fixes reviewed
Address every finding with a code change, a documented acceptance of risk, or a reasoned dispute. Then send the fix commits back for review. A fix written in a hurry under launch pressure is itself new, unaudited code. OpenZeppelin lists a fix review as a standard stage before the final report, and you should insist on one whoever you hire.
Make sure you deploy what was audited
Compare the deployed bytecode against a build of the final reviewed commit. Verify every contract on the block explorer. Check constructor and initializer arguments against the deployment plan. Config errors at deployment (wrong oracle address, wrong decimals, an admin left as a deployer EOA) are not caught by code review.
Publish the report
Publish the final report and the commit it covers. Integrators, listing teams and security-conscious users look for it, and an unpublished report reads as a hidden finding.
Launch a bug bounty
A bounty keeps reviewers looking after the auditors leave. The scale of a good payout is set by what's at risk: Wormhole paid $10 million for one critical report against a contract holding about $736 million. You don't need that number, but the bounty for critical bugs has to be large enough that reporting beats exploiting.
Also consider adopting the SEAL Whitehat Safe Harbor agreement. It sets out in advance the terms under which whitehats can rescue funds during a live exploit, including where to send them and what bounty they keep.
Monitor and rehearse incident response
- Alerts on admin actions, upgrades, pauses, large withdrawals and oracle deviations.
- A written runbook: who can pause, how to reach signers at 3am, which security contacts and exchanges to call.
- A test of the pause and emergency path on a fork, so the first time you run it isn't during an exploit.
Don't forget operational security
Many of the largest losses aren't contract bugs. Chainalysis counted over $3.4 billion stolen in 2025, and the single largest incident, Bybit's roughly $1.5 billion loss, came from a compromised Safe{Wallet} developer machine that served malicious frontend code to signers. Safe's contracts were not affected. Signer devices, frontend hosting, CI secrets and DNS belong in your security plan alongside the audit.
Re-audit changes
Every upgrade, new module or parameter change that touches logic is new code. Scale the review to the diff, but don't skip it. Our Web3 software development process post shows where these reviews fit in the release cycle.
FAQ
How long does a smart contract audit take?
It depends on code size and complexity. A small token or vesting contract can take days, and a lending protocol or DEX can take several weeks with multiple reviewers. Add one to three weeks for fixes and the fix review before mainnet.
What should I send an auditor before the audit starts?
A frozen commit hash, a scope list, build and test instructions, an architecture overview, a specification of intended behavior, your invariants, a roles and privileges table, external dependencies, known issues and any previous reports.
Is one audit enough?
For simple contracts holding modest value, often yes. For protocols holding significant user funds, plan for more than one review, a bug bounty and continuous monitoring. Audited protocols are still exploited regularly, often through code added after the audit.
What's the difference between an audit and a bug bounty?
An audit is a scheduled review of a fixed commit by a hired team before launch. A bug bounty is an open, ongoing offer to pay anyone who reports a vulnerability in live code. You want both.
Can automated tools replace a smart contract audit?
No. Tools like Slither and fuzzers catch known patterns and broken properties you've written down. They don't understand your business logic, economic attacks or the intent behind a function. Run them before the audit so the auditors can focus on what tools miss.
If you want a second pair of eyes before your audit, or a team that writes contracts with the audit in mind from day one, get in touch with 23studio. We can review your scope and documentation and tell you what an auditor will ask for first.
