NanoLab

Audit Preparation

The Ultimate Smart Contract Audit Checklist for DeFi Teams

·12 min read·NanoLab Security Research

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.

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.

1

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.

2

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.

3

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.

4

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.