Audit Preparation
The Ultimate Smart Contract Audit Checklist for DeFi Teams
A smart contract audit checklist is most useful before the auditor ever opens your repository. The strongest teams treat audit preparation as an engineering milestone, not a sales handoff. If you want a serious Solidity security audit, you need more than "the tests pass" and "we use OpenZeppelin." You need clear trust boundaries, explicit invariants, and a release candidate that is stable enough to review deeply.
This guide is written for DeFi founders, protocol teams, and Solidity developers who want a sharper smart contract security review before mainnet or before a major upgrade. Use it as an internal pre-audit checklist, a code review rubric, and a release gate when you audit before deployment. If your team cannot answer these questions confidently, the audit will surface that gap anyway, just later and at higher cost.
Checklist Categories
Before You Hire a Smart Contract Auditor
A clear threat model and protocol spec
Auditors need to know what the system is supposed to protect, who can call what, and which invariants must never break. If those assumptions only live in Slack or in one founder's head, the smart contract security review will spend too much time reconstructing intent instead of testing it.
The exact commit, deployment plan, and scope
Freeze the code you want reviewed. List every in-scope contract, library, proxy, script, and privileged role. Audit before deployment only works if the auditor is looking at the actual release candidate rather than a moving target.
Architecture diagrams and integration dependencies
Show how funds move across vaults, routers, bridges, keepers, and oracle adapters. Many high-severity findings live in the seams between contracts, not inside a single Solidity file.
A strong test suite with invariant or fuzz coverage
A Solidity security audit is faster and deeper when the team already has meaningful unit, integration, and invariant tests. Auditors can validate riskier edge cases instead of spending time on missing baseline coverage.
Privileged operations and emergency procedures
Document who can pause, upgrade, mint, sweep, rebalance, or change risk parameters. Also define the incident-response path if something goes wrong in production. If these controls are vague, the protocol is not really audit-ready.
Check
01
Access Control and Privileged Operations
Most catastrophic findings still begin with authority mistakes. Start your smart contract audit checklist by mapping every actor that can change protocol state or move funds.
Verify every privileged function has explicit authorization
Owner-only, role-gated, or governance-only functions should be obvious from the signature. Hidden authority paths through helper contracts, internal wrappers, or upgrade hooks are easy to miss and expensive to exploit.
Check role separation, not just modifier presence
The same address should not automatically control minting, pausing, upgrades, treasury withdrawal, and oracle changes unless that concentration is intentional. Separation of duties reduces blast radius when one key or signer set is compromised.
Review ownership transfer and admin rotation flows
Two-step ownership transfer, signer rotation, and revocation logic should be tested. A protocol with clean happy-path roles but broken handoff logic can still end up permanently locked or silently hijacked.
Confirm emergency powers are bounded
Pause, rescue, or guardian functions should stop specific risk pathways without becoming a backdoor treasury withdrawal or censorship tool. The more powerful the emergency role, the more carefully it needs to be constrained and documented.
Check
02
Arithmetic, Accounting, and State Invariants
Solidity 0.8 removed silent integer overflow, but it did not remove accounting bugs. Modern audit work is mostly about broken assumptions in balance math, rounding, and state transitions.
Validate mint, burn, deposit, redeem, and fee formulas
Every conversion between shares and assets should preserve the intended invariant. Off-by-one rounding, inconsistent decimal scaling, or fee logic that rounds in the wrong direction can leak value over time or let attackers mint unearned shares.
Trace state updates across multi-step flows
Borrow, liquidate, claim, and rebalance paths often touch several mappings and totals. Check that each state mutation happens once, in the right order, and under the same assumptions as the external event it represents.
Look for edge-case arithmetic at zero or near-zero liquidity
Many inflation and donation attacks rely on tiny initial supply, zero-share states, or first-depositor conditions. If the math only works after the pool has 'normal' liquidity, it is not safe enough for production.
Test invariant drift after repeated operations
A single action may look correct while 1,000 repeated actions slowly break accounting. Run loops over deposits, withdrawals, reward updates, and liquidations to see whether totals still reconcile after stress.
Check
03
Reentrancy and External Call Flow
Any time protocol logic calls an untrusted address, token, hook, or receiver, assume control can come back in an unexpected order. This part of the checklist is non-negotiable for any DeFi protocol.
Enforce checks-effects-interactions ordering
Write internal state before external token transfers, callbacks, or router calls. If balances, debt, or authorization flags are updated after the interaction, the function is a reentrancy candidate even if the code looks clean at a glance.
Review cross-function and read-only reentrancy
The vulnerable path is not always a direct recursive call into the same function. Attackers often re-enter through a different entrypoint that reads stale state or bypasses assumptions made earlier in the transaction.
Check callback-capable integrations
ERC-777, ERC-1363, flash-loan callbacks, safe transfer hooks, and router callbacks all widen the attack surface. If the protocol interacts with assets or contracts that can execute arbitrary code during transfer, model that explicitly in tests and review.
Use guards only where the invariants justify them
A blanket nonReentrant modifier can help, but it should not substitute for correct ordering and state design. Good auditors still ask whether the function would be safe if the guard were removed, because that exposes deeper design risk.
Check
04
Token Standards and Integration Assumptions
Protocols rarely fail because ERC-20 looked strange in the abstract. They fail because a real token behaves differently than the protocol assumed.
Handle non-standard ERC-20 return values and decimals
Some tokens do not return booleans, use unusual decimals, or wrap behavior through upgradeable proxies. Safe integration code should normalize these differences instead of assuming every token behaves like a pristine test asset.
Check fee-on-transfer, rebasing, and deflationary behavior
If the protocol assumes transferred amount equals received amount, accounting may break the first time it touches a fee-charging token. Rebasing assets can also distort collateral checks, share pricing, and liquidation math.
Validate allowance and permit flows
EIP-2612, Permit2, and token-specific approval patterns introduce signature validation and replay concerns alongside normal transfer risk. Audit the approval path with the same rigor as the asset-moving path.
Review token blacklists, pauses, and admin controls
Centralized stablecoins and wrapped assets may freeze addresses or pause transfers. If your protocol depends on those assets, model what happens to withdrawals, liquidations, and solvency when transfers suddenly stop.
Check
05
Oracle Risk and Market Manipulation
A protocol can have perfect Solidity and still fail if it prices assets incorrectly. For lending, derivatives, and collateralized systems, oracle review is core audit work, not an add-on.
Identify the real price source for every sensitive action
Document which prices drive borrowing power, minting, redemption, liquidations, and fee logic. Teams often say 'we use Chainlink' while a fallback path or adapter still relies on a manipulable spot price.
Test low-liquidity and one-block manipulation scenarios
If a price can be moved with a flash loan or a thin pool trade, assume an attacker will do it. Run scenarios where collateral is briefly overpriced or debt is briefly underpriced and see whether the protocol stays solvent.
Check staleness, heartbeat, and fallback behavior
An oracle that stops updating can be just as dangerous as a manipulated one. Decide whether the protocol should pause, revert, or use a guarded fallback when data is stale, missing, or out of bounds.
Review who can change feeds and parameters
Oracle admin controls are security-critical. If a single hot wallet can swap feeds, widen deviation thresholds, or disable circuit breakers, the protocol may be one key compromise away from insolvency.
Check
06
Upgradability, Initialization, and Storage Safety
Upgradeable systems expand the attack surface beyond business logic. If the proxy architecture is wrong, even correct application code can be taken over or bricked.
Make sure implementation contracts cannot be initialized by outsiders
Uninitialized implementations have caused real exploits and permanent loss. Lock them correctly, test deployment scripts, and verify that initialization can only happen through the intended path.
Review upgrade authorization and timelocks
Who can upgrade, how quickly, and through which contract? A proxy controlled by a single signer with no delay is not meaningfully safer than an unreviewed hot patch in production.
Check storage layout compatibility across versions
Variable reordering, inheritance changes, and new storage slots can corrupt balances or admin state silently. Use automated layout diff tooling and manually inspect the riskiest slots before every upgrade.
Audit delegatecall and upgrade hooks carefully
Any delegatecall-based extension or migration path inherits the caller's storage and privileges. That makes helper modules, migration scripts, and initialization hooks part of the security boundary.
Check
07
Gas Limits, Loops, and Denial of Service
A contract does not need to leak funds to fail users. If core actions become too expensive or easy to grief, the protocol can lock, miss liquidations, or fail during stress.
Find unbounded loops in user-facing and admin paths
Anything that iterates over all users, all positions, or all queued actions will eventually hit a gas ceiling. Pagination, batching, or pull-based settlement is usually safer than hoping the data set stays small.
Check for griefable external calls
Push payments, callback-heavy settlement, and sweeping to arbitrary receivers can let one hostile address block an entire flow. The protocol should isolate failures instead of reverting global state transitions whenever possible.
Measure worst-case gas on the busiest code paths
Liquidations, harvests, keeper updates, and emergency actions should be tested with realistic maximum-size inputs. If a function only fits comfortably under normal conditions, it may fail exactly when the market is stressed.
Review block-level assumptions in keeper or batch logic
Some systems assume an operator can always process N positions in one transaction. If that assumption fails, bad debt, stale rewards, or unprocessed redemptions accumulate quickly.
Check
08
Deployment Readiness and Operational Safety
The last category is where many teams underinvest. A strong audit before deployment includes the release process, not just the Solidity source tree.
Verify deployment scripts and constructor arguments
Misconfigured initial roles, wrong oracle addresses, and incorrect token references have caused production incidents even when the audited logic was sound. Review scripts as part of the release artifact, not as an afterthought.
Confirm pause, upgrade, and rollback playbooks
The team should know exactly who acts, with which wallet, and under what threshold if an issue is found after launch. Fast incident response is part of the protocol's real security posture.
Prepare monitoring for invariants and privileged actions
Track abnormal minting, solvency drift, oracle deviations, failed liquidations, and admin calls from day one. A smart contract audit reduces risk, but production monitoring shortens the time-to-detection when assumptions break.
Schedule a final diff review before mainnet or upgrade
If the code changed after the main audit, the release should still get a scoped re-review. Teams lose avoidable money by treating the first report as permanent coverage for later commits.
Final Gate
Use this checklist before the audit, not after the incident
If your team is preparing for mainnet, a treasury-bearing upgrade, or a new DeFi primitive, NanoLab can perform the manual smart contract security review before deployment and help you tighten the release candidate before funds are at risk. See the audit packages, compare scope, and book a review through NanoLab at nanolab.nanocorp.app.