DeFi Intel

Self-Audit Smart Contracts: Beginner Steps

Smart contract security is critical, but hiring a professional audit firm can cost tens of thousands of dollars. For many DeFi projects, a full audit is out of reach – yet deploying unaudited code is reckless. The solution: learn to audit smart contracts yourself using manual review techniques. This guide is written for intermediate Solidity developers who understand the basics but want to systematically find vulnerabilities without relying on automated tools alone.

You’ll learn how to manually inspect source code for the most common attack vectors: reentrancy, integer overflows, privileged access, oracle manipulation, gas griefing, and centralization risks. Each section gives you a concrete checklist of things to look for, code patterns to examine, and mental models to adopt. By the end, you’ll have a repeatable manual audit methodology you can apply to any contract before deployment – and you’ll know when it’s time to bring in the pros.

No magical zero-days, no live prices. Just durable, battle-tested review techniques that work across Solidity versions. Let’s start.

Key takeaways
  • Manual review catches logical flaws that automated tools miss; always read the code yourself after running static analysis.
  • Trace every external call for reentrancy: check that all state changes happen before the call, not after.
  • Scrutinize arithmetic in unchecked blocks and inline assembly – overflows still happen in post-Solidity 0.8 code.
  • Map all privileged roles and their powers; centralization risks should be documented even if not fixed.
  • Use a printed checklist per vulnerability category to ensure systematic coverage during the audit.
  • When in doubt, simulate edge cases in a local testnet (e.g., Foundry) – but never skip the manual line-by-line review.

Before You Begin: Set Up Your Manual Review Environment

To audit smart contracts yourself, you need to read code effectively. First, clone the repository and get the contract running locally with Foundry or Hardhat. Skim the README for architecture, dependencies, and known issues. Then open the source files in an editor that supports syntax highlighting and search (e.g., VS Code with Solidity extensions).

Your goal is to understand every state mutation and every external call. Manual review is about sustained attention, not speed.

Reentrancy: Trace Every External Call

Reentrancy is the most classic vulnerability. Manually audit by finding every call, delegatecall, send, and transfer (or low-level .call()). For each one, ask: have all state changes been made before this call?

Look for the checks-effects-interactions pattern. In the withdraw function of a vault, if the user’s balance is updated after the transfer, that’s a red flag. Examine fallback functions – if a malicious contract re-enters, can it drain funds?

Use a reentrancy guard (OpenZeppelin’s ReentrancyGuard) as a defensive layer, but manual review still requires confirming that the guard is applied to every mutative external function. Also check for cross-function reentrancy where two different functions share state.

Write down every external call in a list, and verify the state-update order. If you find an update-after-call, mark it as a high-severity finding.

Integer Overflows & Underflows: Arithmetic Scrutiny

Solidity 0.8+ has built-in overflow checks, but many projects still use earlier versions or inline assembly where checks are bypassed. Manually inspect every arithmetic operation: +, -, *, /, **, and %.

Checklist:

PatternRiskManual Check
balance[user] -= amount (no safe math)Underflow if amount > balanceIs there a prior require?
totalSupply *= 2 uncheckedOverflow if totalSupply > max/2Check max supply or use SafeMath

Access Control: Who Can Do What at Each Step?

Manual access control review means tracing every onlyOwner, require(msg.sender == ...), and role-based modifier. Print the contract and draw a table: function name → which addresses can call it → under what condition.

Common pitfalls:

Also examine delegatecall based proxies: the implementation’s initialize() should not be callable by anyone. Use OpenZeppelin’s Initializable and verify that the logic contract’s constructor disables initializers.

Oracle Manipulation: Always Check the Data Source

Price oracles are a frequent attack surface. Manually audit any place where an external price feed is used (Uniswap TWAP, Chainlink, custom oracles). Key questions:

Manual review tip: trace the price feed all the way to how it’s used in a financial calculation (e.g., loan-to-value). If the price can be distorted by a single transaction, the contract is vulnerable to liquidation attacks.

Gas Griefing & Unbounded Loops: Scrutinize Loop Iteration

Unbounded loops that iterate over arrays or user-supplied data can cause out-of-gas errors and griefing. Manually examine every for and while loop:

Also look for nested loops – they multiply gas consumption exponentially. A manual reviewer should calculate worst-case gas for each function. If you find a function that could consume more than the block gas limit, flag it as denial-of-service.

Trust Assumptions & Centralization: Map the Admin Keys

Centralization is not a vulnerability per se, but it’s a risk every user must understand. Manually map all privileged roles: owner, pauser, minter, proxy admin, fee setter, etc. List what each can do:

Create a trust matrix in your notes: for each privileged function, write down the worst-case action an admin could take. Then decide if that risk is acceptable given the project’s governance model. Many exploits happen because a single EOA had unchecked power – manual review exposes those assumptions.

Manual Audit Checklist: A Practical Triage Sheet

After reviewing each vulnerability category, compile a one-page checklist. Print it and go through every function in the contract. Example checklist:

CategoryCheck ItemPass/Fail/Note
ReentrancyState written before every external call?
OverflowUnchecked blocks? Assembly? Pre-0.8?
Access ControlEvery critical function has a modifier?
OracleTwap or freshness check present?
GasNo unbounded loops? Worst-case gas < block limit?
CentralizationAdmin key? Multisig? Timelock?

This systematic approach ensures you don’t miss the low-hanging fruit. Manual review is not a replacement for a professional audit, but it drastically reduces the chance of a catastrophic bug. Repeat the process for every deployed contract version.

Frequently asked questions

How long does a manual self-audit take for a typical DeFi contract?

Expect at least 4-8 hours for a contract under 500 lines if you are methodical. Complex vaults or AMMs can take 15-20 hours. Speed comes with practice, but quality requires sustained focus.

Should I rely only on automated tools like Slither instead of manual review?

No. Automated tools are great for catching known patterns (reentrancy, overflow) but miss business logic flaws, incorrect assumptions, and centralization risks. Manual review is essential to understand the contract's full risk profile.

What if I find a vulnerability in my own contract?

Fix it, re-test, and consider if the vulnerability could have been exploited. If it's a logic flaw, write additional test cases. For high-severity bugs, you may still want a professional auditor to verify the fix.

Do I need to understand Solidity assembly to audit effectively?

Yes, at least enough to read common patterns like delegatecall, staticcall, and arithmetic opcodes. Many vulnerabilities hide in assembly blocks that bypass compilers checks.

Track the entities behind the concepts

DeFi Intel maps 11,000+ protocols, tokens and companies to a typed knowledge graph — with live data, incidents and regulation.

Entities mentioned