Roadmap › Solidity & Smart Contract Development › Module: Advanced Topics
Lessons in this course (11)

8. Security vulnerabilities and defences

Advanced★ Must knowLesson 8 of 11
VulnerabilityWhat happensDefence
ReentrancyExternal call re-enters your function before state is updated (The DAO, 2016)Checks-Effects-Interactions, ReentrancyGuard (nonReentrant), pull payments
Access control bugsMissing onlyOwner/role checks, unprotected initializeExplicit modifiers, tests for every privileged function, OZ AccessControl
tx.origin authPhishing contract calls yours on behalf of the userUse msg.sender
Integer overflowPre-0.8 wrap-around (BEC token 2018)Solidity ≥ 0.8 (checked), careful unchecked
Oracle manipulationSpot price from one pool manipulated (often with flash loans)Chainlink/TWAP, multiple sources, sanity bounds
Front-running / MEVAttackers see pending txs and reorderCommit-reveal, slippage limits, batch auctions
Denial of serviceUnbounded loops, failing recipient blocks payoutsPagination, pull payments, avoid loops over user arrays
Signature replaySame signature reused (other chain/contract/after use)Nonces, chainid, EIP-712 domain separator, deadlines
Unchecked return valuesIgnoring call result or non-standard ERC-20s (USDT returns nothing)Check ok; use SafeERC20
delegatecall / proxy storage collisionsLogic contract overwrites proxy storageOZ upgradeable patterns, storage gaps / ERC-7201 namespaced storage, _disableInitializers()
Randomnessblock.timestamp/prevrandao predictable/influenceableChainlink VRF
Precision lossDivide before multiplyMultiply first, use fixed-point libraries
Private dataprivate vars readable from storageNever 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
    }
}