Every Web3 protocol that gets hacked makes one of two mistakes. Either it skipped an audit entirely, or it walked into the audit with code that needed weeks of cleanup before the auditors could even start. The second mistake is the more expensive one. You pay senior auditors at $400 an hour to point out things any junior dev could have caught.
This is the checklist we run on every contract before it leaves our studio. Tick all 14 and your audit will go faster, cost less, and come back with fewer findings. Skip any of them at your own risk.
01 Why pre-audit matters
Audit firms charge by complexity and by how much fixing they have to do. A clean codebase that just needs a security review costs less. A messy codebase where the auditor finds basic issues on day one costs more — partly because they have to write up findings, partly because every fix needs a re-review, and partly because they quote higher rates for code they expect to be a mess.
More importantly: a clean codebase finds fewer real issues. The kind of bugs that lose money to attackers tend to hide behind sloppy patterns. Tighten the patterns first, and the dangerous bugs are easier to spot.
02 The 14 checks, in the order we run them
We do these in roughly this order because each one makes the next one easier. Skipping ahead works, but you will end up redoing earlier ones.
-
01
Compiler version pinned
Use
pragma solidity 0.8.24;not^0.8.0. A floating compiler version means the contract auditors test is not necessarily the one that gets deployed. Pin to an exact patch version. This is a thirty-second change that auditors flag as a finding if you skip it. -
02
Checked math (Solidity 0.8+ default, but verify)
Solidity 0.8 and up has built-in overflow checks, so SafeMath is no longer needed. But check that you are not using
unchecked { }blocks unless you have a real reason. Everyuncheckedblock needs a comment explaining why it is safe. -
03
Reentrancy guards on every external call
Any function that transfers ETH, calls another contract, or performs a token transfer needs to be protected. Use
nonReentrantfrom OpenZeppelin’sReentrancyGuard, and pair it with the CEI pattern (check 05). Reentrancy is still the most common cause of real-money exploits. -
04
Access control on every privileged function
Walk every function that changes state. Ask: should anyone be able to call this? If the answer is no, it needs an
onlyOwneror a role-based modifier. Missing access control is the easiest hack on earth, and it still happens to live protocols every quarter. -
05
CEI pattern (Checks-Effects-Interactions)
Every function that does external calls follows the same order: check requirements first, update state second, call external contracts last. If you call an external contract before updating your own state, you are giving an attacker a window to re-enter and double-spend. CEI closes that window.
-
06
Custom errors instead of revert strings
Replace
require(condition, "error message")withif (!condition) revert ErrorName();. Custom errors cost less gas and read better. Every modern Solidity codebase should be on custom errors. Auditors expect it. -
07
Slither and Mythril clean
Run static analysis tools before you book the audit. Slither and Mythril are free and catch the obvious problems that auditors should not be wasting time on. Aim for zero high-severity findings on Slither before sending the code to a paid firm.
-
08
100% branch coverage in tests
Line coverage is not enough. Branch coverage tracks every
ifbranch and every conditional path. Useforge coverageor Hardhat’s coverage plugin. Anything below 100% on critical paths means you have code that has never been executed. That is where bugs live. -
09
Fuzz tests on input-bounded functions
Use Foundry’s built-in fuzzing or Echidna for property-based testing. Throw a few thousand random inputs at every function that takes user input. Fuzzing finds the edge cases your unit tests forgot about. One hour of fuzz setup saves you from a $5M exploit.
-
10
Invariant tests for protocol-level rules
Invariants are statements that should be true forever regardless of how the contract is used. Examples: total supply equals the sum of balances, the contract never holds less ETH than it owes users. Foundry has good invariant test tooling. Every protocol should have at least three.
-
11
Gas snapshot in CI
Track gas costs per function in your test suite. Commit a snapshot file. If a code change blows up gas usage by 30%, your CI should fail. Gas regressions are sometimes accidental and sometimes intentional — either way you want to see them before they hit mainnet.
-
12
Storage layout documented
For upgradable contracts especially, the storage layout matters more than the code. Document which slot holds what. Use OpenZeppelin’s upgrade plugin to detect storage collisions. A storage collision on an upgrade can brick the contract permanently.
-
13
Deployment scripts archived and source verified
Save your deployment script in the repo, with the exact constructor arguments and network used. Verify the source on Etherscan or whatever block explorer applies. Unverified contracts make users nervous and make incident response harder. There is no good reason to skip verification.
-
14
Pause and upgrade plan documented
Write down what happens when something breaks. Who can pause the contract? Who can upgrade it? What is the timelock? What is the multisig threshold? If your only answer is “the founder will fix it,” that is a finding. Document the plan, even if the plan is “immutable, no upgrades possible” — that is also a valid plan.
03 What this saves you in dollars
A typical Web3 audit for a mid-complexity protocol runs $25,000 to $80,000. The variation is mostly driven by code quality going in. We have seen the same firm quote $40,000 for a clean codebase and $90,000 for a messy one of the same size, with the messy one also taking three weeks longer.
Beyond the audit cost itself, the bigger savings come post-launch. Each finding that lands in the public audit report is a piece of ammunition for users to question the protocol. Each fix between audits costs another round of review. Each missed bug becomes the news story. Pre-audit work pays for itself ten times over.
04 What auditors actually look for, in order
For context, here is what most reputable audit firms check first when they receive a codebase. If you are missing the items at the top, fix those before you send anything:
- Access control on privileged functions — can a stranger drain the contract?
- Reentrancy on external calls — can someone re-enter mid-transfer?
- Arithmetic edge cases — overflow, underflow, division-by-zero, rounding tricks.
- Front-running and MEV exposure — can a bot reorder your transaction profitably?
- Oracle manipulation — if you read prices, can someone push them?
- Upgrade and storage layout — only relevant for proxies, but critical when relevant.
- Centralisation risk — what can the admin do? What stops them?
All seven of those map directly to the 14 checks above. The checklist is not arbitrary. It is the same list auditors run, just run earlier and by you.
05 Tools we actually use
None of these are exotic. They are all free or very cheap.
- Foundry for testing, fuzzing, and gas snapshots. Replaced Hardhat in our workflow two years ago.
- Slither for static analysis. Run on every commit.
- Echidna for property-based fuzz testing. Slower setup than Foundry but catches different bugs.
- OpenZeppelin Contracts for the building blocks — do not roll your own ERC20.
- OpenZeppelin Upgrades if you ship proxies, for storage layout safety.
- solc-select to pin compiler versions across machines on a team.
06 How long does this take?
On a fresh contract written with these patterns from day one, the full checklist takes about a week of focused work after the feature code is done. On a legacy contract being prepped for audit, it can take three weeks. Either way, that is time you would have spent anyway — just before the audit, instead of during it at four times the hourly rate.
Worth every hour.
We do this work for protocols every quarter.
If you have a smart contract heading for an audit, we can run this checklist and the cleanup before you book the audit firm. Faster audit, fewer findings, lower total cost. Worth a call.