Audits, Bug Bounties & Security Reviews
Over $800M in bug bounties are currently active across DeFi protocols. But bounties only matter if the protocol was audited first. This guide covers how audits work, what the top firms actually test, how to read a report yourself, and the difference between formal verification and manual review.
🏆 Top DeFi Security Audit Firms
Trail of Bits
Most respected in the industry for complex DeFi protocols. Their reports are detailed and often identify logic-level bugs that automated tools miss. Publication record is unmatched.
OpenZeppelin
Built the most widely-used smart contract library in DeFi. Their audits focus heavily on access control, upgrade patterns, and integration risks. Most audited project in DeFi.
Certik
Uses heavy AI/ML-based static analysis. Massive audit volume — covers most new DeFi launches. Skynet monitors contracts post-deployment for anomalies. Controversial for speed vs depth tradeoffs.
Spearbit
Smaller, elite team focused on complex protocol architecture. Particularly strong on MEV-sensitive systems, DEX primitives, and novel financial primitives. Lower volume, highest per-engagement depth.
📄 Anatomy of an Audit Report
Every major audit report follows the same structure. Here's what each section tells you.
1. Executive Summary
High-level overview: what was audited, methodology used, total findings, severity breakdown. Read this first to gauge overall risk. If a protocol says 'clean audit' but the report shows 3 Critical, that's misleading.
2. Scope & Methodology
What code was in scope, what was explicitly excluded, what review methods were used (manual, automated, fuzzing, formal verification). The methodology tells you how deep the review went.
3. Findings & Recommendations
The core of the report. Each finding has: severity (Critical/High/Medium/Low), description, code reference (file:line), impact analysis, recommended fix. This is where you learn what went wrong.
4. Resolution & Remediation
What the protocol fixed after the audit. This section shows whether findings were actually addressed — some reports show findings but don't confirm resolution.
🔬 Formal Verification vs Manual Review
🔬 Formal Verification
Mathematical proof that code satisfies its specification — for specific invariants like 'total supply never exceeds max' or 'balance can only decrease via transfer'
Finds edge cases unit tests miss. Proves absence of entire bug classes. Computer-checkable proof — no subjective judgment.
Only as good as the specification. Assumes certain conditions (e.g., 'owner is always honest') that may be false. Doesn't find logic errors in the spec itself.
$100K–$500K+ — significantly more expensive than manual audit. Reserved for protocols holding $500M+.
Runtime Verification, Certora, Trail of Bits (formal), Chainsafe
👥 Manual Review
Experienced auditors have reviewed the code and found no obvious vulnerabilities — based on pattern knowledge and adversarial thinking.
Finds logic bugs, integration issues, novel attack vectors. Context-aware — understands how the protocol fits into the broader DeFi ecosystem. Cheaper and faster.
Human limitation — auditors can't review every possible state. Subjective quality varies. Different auditors find different things.
$15K–$150K depending on protocol complexity and firm reputation.
Trail of Bits, OpenZeppelin, Spearbit, Consensys Diligence, Sherlock
🎯 Bug Bounty Programs
Bug bounties are ongoing security programs that pay researchers for vulnerabilities found in live code. The industry standard is Immunefi — over $800M in bounties listed there as of 2025.
Immunefi
Largest Web3 bug bounty platform. 200+ projects, $800M+ in bounties. Payouts range from $500 (low severity) to $2.5M+ (critical). Top programs: OpenZeppelin ($2.5M max), Chainlink ($1.5M), Uniswap ($1M+).
HackerOne
Legacy Web2/Web3 platform. Used by Consensys, some CeFi projects. Smaller Web3 focus than Immunefi but established reputation. Payouts typically $100–$100K.
Internal / GitHub
Some protocols run their own bounty programs via GitHub issues or direct researcher relationships. These are harder to verify and don't offer the same dispute resolution as platforms. Look for terms on the protocol's security page.
What Bug Bounties Cover (vs Audits)
✅ Protocol Security Evaluation Checklist
Use this checklist when evaluating any DeFi protocol. Each item adds to the security score. A score below 70% means elevated risk.
🔍 Audit Quality
🐛 Bounty Program
🔑 Access Control
📐 Code Quality
📖 How to Actually Read an Audit Report
Start with severity distribution, not individual findings
A protocol with 0 Critical, 3 High is far better than one with 2 Critical. The counts tell you the overall risk profile. Individual findings matter, but the aggregate severity is what you should weight most heavily.
Find 'Unresolved' and 'Accepted Risk' findings
Any Critical finding marked 'Unresolved' or 'Accepted' should be a red flag. 'Accepted Risk' means the protocol explicitly decided the cost of fixing it exceeds the probability-weighted impact — that's a business decision, but you should know about it. For High severity, check if mitigations are documented.
Cross-reference with on-chain activity
An audit from 2 years ago for a protocol that's since had major upgrades is partially stale. Check if there have been subsequent audits covering newer code. If a protocol went from V1 to V2 and only V1 was audited, that's a gap. Look for 're-audit after upgrade' mentions.
Check the fix, not just the finding
Auditors describe vulnerabilities. The protocol's response shows what they actually fixed. A finding that says 'protocol argues this is not exploitable' without a code change is different from one where the protocol actually changed the logic. The best reports show before/after code.
Look for systemic patterns
If a protocol has 5 findings all related to 'access control' patterns, that's a deeper issue than 5 isolated findings in unrelated areas. A systematic issue suggests the development team's security practices are underdeveloped — not just individual oversights.
📄 Example: Trail of Bits Finding Format
// Fixed code:
function withdraw(uint256 assets) external nonReentrant returns (uint256) {
uint256 shares = convertToShares(assets);
// ✅ Effects FIRST
_burn(msg.sender, shares);
// State updated before external call
asset.transfer(msg.sender, assets);
emit Withdraw(msg.sender, assets, shares);
}