Smart Contract Auditing 101: Identifying Common EVM Vulnerabilities
A developer and investor guide to reviewing Solidity smart contracts, spotting reentrancy bugs, access control flaws, and oracle manipulation risks.
Introduction: The Philosophy of Code as Law
On public blockchains, deployed bytecode is immutable. Flaws in logic or state updates cannot be patched through traditional hotfixes without pre-engineered proxy architectures. Conducting systematic static analysis and manual code review is mandatory prior to depositing capital into any DeFi protocol.
1. Reentrancy Vulnerabilities
Reentrancy occurs when an external contract call hands execution control back to an untrusted caller before state updates are finalized.
Vulnerable Pattern:
// VULNERABLE: State updated after external ether transfer
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient funds");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount; // State modified too late!
}
Remediation: Checks-Effects-Interactions Pattern
// SECURE: Checks-Effects-Interactions & ReentrancyGuard
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient funds");
balances[msg.sender] -= amount; // 1. Effect
(bool success, ) = msg.sender.call{value: amount}(""); // 2. Interaction
require(success, "Transfer failed");
}
2. Oracle Manipulation & Spot Price Reliance
Protocols that query instantaneous spot prices from decentralized exchanges (like Uniswap v2 pair reserves) are vulnerable to flash loan manipulation within a single transaction block.
| Oracle Strategy | Vulnerability Level | Recommended Implementation |
|---|---|---|
| DEX Spot Reserves | Critical | NEVER use directly for lending collateralization |
| TWAP (Time-Weighted Average) | Moderate | Requires multiple blocks, costly to manipulate |
| Decentralized Oracles (Chainlink) | Low | Multi-source aggregation with heartbeat checks |
3. Access Control & Initialization Flaws
Unprotected administrative initializers in upgradeable contracts (UUPS / Transparent Proxies) allow attackers to claim contract ownership:
- Ensure all
initialize()functions possess theinitializermodifier. - Verify multi-signature governance (
Gnosis Safe) controls privileged roles. - Implement timelocks with a minimum 48-hour delay for critical parameter modifications.
Audit Checklist for Protocol Due Diligence
- Independent audit reports published by accredited security firms
- Bug bounty program active on Immunefi
- No unmitigated reentrancy vectors across all state-mutating functions
- Decentralized oracle pricing resistant to single-block flash loan arbitrage
- Multi-sig administrative access guarded by public timelock