Roadmap › Solidity & Smart Contract Development › Module: Advanced Topics
Lessons in this course (11)
8. Security vulnerabilities and defences
Advanced★ Must knowLesson 8 of 11
| Vulnerability | What happens | Defence |
|---|---|---|
| Reentrancy | External call re-enters your function before state is updated (The DAO, 2016) | Checks-Effects-Interactions, ReentrancyGuard (nonReentrant), pull payments |
| Access control bugs | Missing onlyOwner/role checks, unprotected initialize | Explicit modifiers, tests for every privileged function, OZ AccessControl |
tx.origin auth | Phishing contract calls yours on behalf of the user | Use msg.sender |
| Integer overflow | Pre-0.8 wrap-around (BEC token 2018) | Solidity ≥ 0.8 (checked), careful unchecked |
| Oracle manipulation | Spot price from one pool manipulated (often with flash loans) | Chainlink/TWAP, multiple sources, sanity bounds |
| Front-running / MEV | Attackers see pending txs and reorder | Commit-reveal, slippage limits, batch auctions |
| Denial of service | Unbounded loops, failing recipient blocks payouts | Pagination, pull payments, avoid loops over user arrays |
| Signature replay | Same signature reused (other chain/contract/after use) | Nonces, chainid, EIP-712 domain separator, deadlines |
| Unchecked return values | Ignoring call result or non-standard ERC-20s (USDT returns nothing) | Check ok; use SafeERC20 |
delegatecall / proxy storage collisions | Logic contract overwrites proxy storage | OZ upgradeable patterns, storage gaps / ERC-7201 namespaced storage, _disableInitializers() |
| Randomness | block.timestamp/prevrandao predictable/influenceable | Chainlink VRF |
| Precision loss | Divide before multiply | Multiply first, use fixed-point libraries |
| Private data | private vars readable from storage | Never store secrets on-chain; commit hashes |
solidity
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract Vault is ReentrancyGuard {
using SafeERC20 for IERC20;
IERC20 public immutable token;
mapping(address => uint256) public shares;
constructor(IERC20 t) { token = t; }
function withdraw(uint256 amount) external nonReentrant {
require(shares[msg.sender] >= amount, "Too much"); // checks
shares[msg.sender] -= amount; // effects
token.safeTransfer(msg.sender, amount); // interactions
}
}