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.
- 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).
- Read from bottom up: Start with internal/private functions and helper libraries to understand assumptions before looking at public entry points.
- Print the code, annotate it physically – research shows manual annotation catches patterns that scrolling on screen misses.
- Run static analysis (Slither, Mythril) after your first pass, but don’t rely on it. Automated tools give false positives and miss logical flaws.
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:
- In
uncheckedblocks, overflow is not detected. If you seeunchecked { ... }, assume all arithmetic inside can overflow unless proven otherwise with explicit bounds checks. - In assembly (
add,sub,mul), there are no guards. Audit these carefully, especially in token contracts. - Division before multiplication can cause precision loss. Look for
a / b * cwherea/btruncates – should bea * c / b.
| Pattern | Risk | Manual Check |
|---|---|---|
balance[user] -= amount (no safe math) | Underflow if amount > balance | Is there a prior require? |
totalSupply *= 2 unchecked | Overflow if totalSupply > max/2 | Check 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:
- Initializers: Can
initialize()be called again after deployment? Look forinitializermodifier usage or a reinitialization check. - Selfdestruct: Only an admin should call
selfdestruct, but is there a timelock or multisig requirement? If an admin key gets compromised, the contract can be killed. - Pause/Unpause: Can anyone unpause? Check the modifier and the owner’s address – is it a multisig or an EOA?
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:
- Is the oracle’s data fresh? For Chainlink, check
updatedAtand compare withstalePeriod. Manual code review must confirm the freshness check exists and its constants. - Is the oracle a spot price from a single liquidity pool? That’s manipulable via flash loans. Prefer TWAP (time-weighted average price) over the last N blocks.
- Can the admin switch the oracle? If yes, does that require a timelock? An admin replacing a Chainlink oracle with a manipulated pool oracle is a centralization risk.
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:
- Can the loop length be arbitrarily large? If it iterates over a dynamic array that grows without limit, a malicious user can inflate the array to cause the transaction to revert or cost excessive gas.
- Does the loop make external calls? Each external call costs extra gas and can be a grief vector if the called contract reverts.
- Is there an escape hatch? Some protocols let users push to arrays without a corresponding pop. Check for a function to remove items or a maximum length check.
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:
- Can the owner withdraw all user funds? If yes, mark as centralization risk.
- Can the owner upgrade the contract? Check if the proxy contract uses a transparent or UUPS proxy and who holds the admin key.
- Is there a timelock? Without a timelock, an admin key compromise can drain the protocol instantly. Manual review should note the absence of any delay.
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:
| Category | Check Item | Pass/Fail/Note |
|---|---|---|
| Reentrancy | State written before every external call? | |
| Overflow | Unchecked blocks? Assembly? Pre-0.8? | |
| Access Control | Every critical function has a modifier? | |
| Oracle | Twap or freshness check present? | |
| Gas | No unbounded loops? Worst-case gas < block limit? | |
| Centralization | Admin 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.
Related reading
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.