The Smart Contract Audit Checklist I Actually Use (After 200+ Audits)

Blockchain White Hackers  Security Audits   The Smart Contract Audit Checklist I Actually Use (After 200+ Audits)

The Smart Contract Audit Checklist I Actually Use (After 200+ Audits)

TL;DR: After 200+ audits and 27 years in security, here’s the exact checklist I use before signing off on a smart contract. It’s not comprehensive (no checklist is), but it covers the vulnerabilities that actually drain money from real protocols.

Most audit checklists are useless. They’re 200 items long, half of which don’t apply to your specific contract. You check the boxes, move on, and miss the vulnerability that costs $50M.

Here’s my actual workflow. It’s not academic. It’s based on what I’ve seen get exploited in production.

My Audit Process: Old School Meets AI

After 27 years, my process hasn’t changed in its core philosophy — but AI has transformed how I execute it. Here’s the real workflow:

The Old School Foundation (Still Essential)

Step 1: Documentation. Read everything. Whitepaper, specs, comments, README. Understand what the protocol is supposed to do before you look at how it does it.

Step 2: Architecture. Map the contract relationships. Who calls whom. Where the money flows. Where the trust assumptions are. Draw it out if you have to.

Step 3: Line by line. Yes, every line. There’s no shortcut for this. You read the code the way a compiler reads it — function by function, state change by state change. This is where 90% of vulnerabilities get found.

Step 4: Tests. Write tests for what you found suspicious. Prove it or disprove it. Foundry makes this fast.

Step 5: Automated tools. Slither, Echidna, Mythril — run them last, not first. They’re a safety net, not a strategy. They catch what your eyes missed at 3 AM.

How AI Changes the Game

Here’s the thing: AI doesn’t replace any of these steps. But it transforms Step 1 and Step 2 completely.

Before AI, understanding a new 5000-line codebase took me a full day. Now I feed it to Claude and get a complete architecture analysis in 10 minutes — contract relationships, inheritance chains, external dependencies, trust boundaries, all mapped out. It gives me starting points: “this function has unbounded loops,” “this oracle has no staleness check,” “this access control pattern differs between these two contracts.”

That doesn’t mean I skip my own analysis. But instead of spending 8 hours building context, I spend 1 hour verifying and deepening what AI found. The other 7 hours go into the deep work — the line-by-line, the creative attack vectors, the economic modeling.

The result: same thoroughness, 2-3x the speed. Or better yet — same time, 2-3x the depth. I catch things now that I would have missed before, simply because I have more time for the hard parts.


Phase 0: Pre-Audit Setup (Before Reading Code)

Document Review

  • ☐ Read the spec/design document (if one exists)
  • ☐ Understand the business logic and intended behavior
  • ☐ Identify what should NEVER happen (invariants)
  • ☐ Map out all external dependencies and trust assumptions
  • ☐ List known risks the team is accepting

Scope Definition

  • ☐ Which contracts are in scope?
  • ☐ Which external calls are expected vs. dangerous?
  • ☐ What’s the attack surface (who can call what)?
  • ☐ What are the economic assumptions (oracle prices, liquidity)?
  • ☐ What’s NOT in scope (and why)?

This phase is critical. If you don’t understand what the code is SUPPOSED to do, you can’t find what’s wrong with it.

Phase 1: Automated Scanning (1-2 hours)

Static Analysis

  • ☐ Run Slither: slither .
  • ☐ Run Aderyn: aderyn .
  • ☐ Review all findings (don’t dismiss as false positives yet)
  • ☐ Triage by severity and likelihood
  • ☐ Flag anything that touches external calls or state changes

Coverage and Complexity

  • ☐ Generate coverage report: forge coverage
  • ☐ Are lines with low/no coverage critical to security?
  • ☐ Count lines of code (simple metric but useful)
  • ☐ Estimate required audit depth (10KB vs. 100KB vs. 500KB codebase)

If coverage is below 85%, demand more tests before proceeding. Untested code is unaudited code.

Phase 2: Manual Code Review (Days – Depth Depends on Codebase)

Entry Points: What Functions Can Be Called?

  • ☐ List every public/external function
  • ☐ For each: Who can call it? (access control check)
  • ☐ What state does it modify?
  • ☐ What external calls does it make?
  • ☐ Does it validate all inputs?
  • ☐ Can it be called with zero/max/negative values?

Control Flow: Can Functions Interact Dangerously?

  • ☐ Is there reentrancy between functions? (checks-effects-interactions)
  • ☐ Can state be accessed before being initialized?
  • ☐ Are there race conditions between transactions?
  • ☐ Can modifiers be bypassed?
  • ☐ Are there implicit assumptions about call order?

State Management: Is State Consistent?

  • ☐ Can total supply exceed actual balance?
  • ☐ Can user balance exceed total?
  • ☐ Are there state invariants that can be violated?
  • ☐ Is state updated atomically or in steps?
  • ☐ Can emergency withdrawals leave state inconsistent?

External Calls: The Biggest Risk Category

This is where the money leaks. Every external call is a potential vulnerability.

  • ☐ List every external call (.call, .transfer, delegatecall, token transfer)
  • ☐ For each: Is state updated before or after?
  • ☐ Can the recipient reenter? (assume yes)
  • ☐ Is the return value checked?
  • ☐ Can the call fail silently (low-level call without check)?
  • ☐ Is gas forwarded? (could enable DoS)
  • ☐ Are tokens transferred safely (SafeERC20 used)?
  • ☐ Could a token transfer fail in ways the code doesn’t handle?
// RED FLAG: External call before state update
recipient.call{value: amount}("");  // What if this reenters?
balances[recipient] -= amount;      // Too late!

// SAFE: State first, then call
balances[recipient] -= amount;      // Update state
(bool success, ) = recipient.call{value: amount}("");
require(success, "Transfer failed");

Arithmetic and Overflow/Underflow

  • ☐ Solidity version: 0.8.0+ (has built-in overflow protection)?
  • ☐ If pre-0.8: Are SafeMath or unchecked used correctly?
  • ☐ Any multiplication before division? (precision loss)
  • ☐ Boundary values: What if amount = 0? max? max-1?
  • ☐ Percentage calculations: Are they accurate?
  • ☐ Any unchecked blocks? Are they justified?

Access Control

  • ☐ Every sensitive function has access control?
  • ☐ Using msg.sender not tx.origin?
  • ☐ Roles/permissions are properly enforced?
  • ☐ Admin keys: How many? Where stored? Who has access?
  • ☐ Can a hacked admin key compromise the protocol?
  • ☐ Is there a way to recover from key compromise?
  • ☐ Upgradeable contracts: Who can upgrade?

Integrations and Dependencies

  • ☐ What external contracts does this depend on?
  • ☐ What happens if they behave unexpectedly?
  • ☐ Are dependencies using safe patterns (e.g., Checks-Effects-Interactions)?
  • ☐ Could dependency become malicious/compromised?
  • ☐ Oracles: Single source or aggregated?
  • ☐ Can oracle be manipulated? (flash loans, sandwich attacks)
  • ☐ Token transfers: What if token doesn’t return bool?
  • ☐ What if token has transfer fees or hooks?

Phase 3: Testing and Fuzzing

Foundry Testing

  • ☐ Run existing tests: All pass?
  • ☐ Coverage > 85%?
  • ☐ Write tests for suspicious functions
  • ☐ Test boundary values (0, 1, max, max-1)
  • ☐ Test with zero-value calls
  • ☐ Test reentrancy by calling from malicious contract
  • ☐ Test with different token implementations
  • ☐ Fork mainnet and test with real state

Fuzzing

  • ☐ Run Foundry fuzz: forge test --fuzz-runs 50000
  • ☐ Define invariants (properties that must always be true)
  • ☐ Run Echidna for 24+ hours on critical contracts
  • ☐ Any failing invariants = bugs
  • ☐ Verify fuzzer can trigger all code paths

Exploit Development

  • ☐ For suspected vulnerabilities: Write full exploit PoC
  • ☐ Verify on forked mainnet (not just testnet)
  • ☐ Calculate exact dollar amount at risk
  • ☐ Document attack path in detail

Phase 4: Economic and Game Theory Review (For DeFi)

Price and Value Assumptions

  • ☐ What prices are assumed? (collateral, borrowed asset, etc.)
  • ☐ What if price falls 50%? 90%?
  • ☐ What if liquidity disappears?
  • ☐ Can liquidations cascade and spiral?
  • ☐ Are there unprofitable liquidations?

Incentive Alignment

  • ☐ Who benefits from what actions?
  • ☐ Are there perverse incentives? (borrow, don’t repay)
  • ☐ Can MEV bots exploit the protocol?
  • ☐ Can liquidators grief users unfairly?
  • ☐ Can governance be exploited?

Extreme Scenarios

  • ☐ What if TVL drops 90%? (liquidity crisis)
  • ☐ What if major token goes to zero?
  • ☐ What if oracle is down for 24 hours?
  • ☐ What if there’s a flash loan attack?
  • ☐ What if a major user withdraws everything?
  • ☐ Stress test with mainnet-like conditions

Phase 5: Final Verification

  • ☐ Every finding has a PoC or detailed explanation
  • ☐ Severity ratings are justified (why is this critical vs. high?)
  • ☐ No findings are contradictory
  • ☐ Team has a reasonable response plan
  • ☐ Post-deployment monitoring plan exists
  • ☐ Bug bounty program in place before mainnet?

The Red Flags (Things That Make Me Dig Deeper)

  • No tests or low coverage: How do they even know it works?
  • Vague documentation: If they can’t explain it, I don’t trust it
  • Recently forked code: Probably inherited vulnerabilities
  • Complex nested logic: Bugs hide in complexity
  • Admin functions without rate limiting: Too much centralized power
  • No access control on critical functions: Immediate failure
  • Raw external calls (.call{}): Reentrancy is almost guaranteed
  • Spot price oracles: Will be exploited with flash loans
  • Token assumptions: “All tokens work like USDC” — they don’t
  • No upgradeable contract strategy: Will need to upgrade anyway, plan for it

Tools I Actually Use (In Order of Importance)

  1. Foundry: Testing, fuzzing, mainnet forking
  2. Slither: Static analysis
  3. Echidna: Deep fuzzing for critical contracts
  4. Tenderly: Transaction tracing and simulation
  5. My brain: Understanding what the code is SUPPOSED to do

Everything else is supplementary.

What This Actually Takes (Realistic Timeline)

  • Small contract (< 500 lines, single protocol): 3-5 days
  • Medium (500-2000 lines, complex logic): 1-2 weeks
  • Large (2000+ lines, multiple dependencies): 2-4 weeks
  • Enterprise (100K+ lines, complex upgrade paths): 4-8 weeks

If someone promises an audit in 2 days, they’re skipping steps. Likely critical ones.

The Bottom Line

This checklist isn’t gospel. Every contract is different. But after 200+ audits, this workflow catches the vulnerabilities that actually cost money.

The protocols that never get hacked follow something like this process. The ones that do get exploited skipped phases 3 and 4, trusted automated tools too much, or ignored red flags.

Security is a process, not a destination. This is the process I use.

Stay safe out there.

Want a deeper dive into smart contract security practices? Check out blockchainwhitehackers.com

Disclaimer: This article was researched and written by members of BWH Academy, with AI-assisted research and drafting. While we strive for accuracy, details may slightly differ from exact real-world scenarios. All content is provided for educational and learning purposes only — not as professional security advice.

No Comments
Leave a Comment:
Skip to main content