Smart contract exploits cost the DeFi ecosystem billions every year — and the same vulnerability classes appear again and again. Understanding them is the first step to writing code that doesn't become a cautionary tale.

This list is drawn from real exploit data across Ethereum, BSC, Polygon, and Solana in 2024–2025. Each entry includes how the vulnerability works, a minimal code example, and what to do instead.

Is Your Contract Vulnerable?

Paste your contract address for an instant AI-powered scan — checks all 10 vulnerability classes below and more.

#1 — Reentrancy

Severity: Critical | Responsible for hundreds of millions in losses including The DAO ($60M).

A reentrancy attack occurs when an external contract is called before the caller's state is updated, allowing the external contract to recursively re-enter the function.

// VULNERABLE
function withdraw(uint amount) external {
    require(balances[msg.sender] >= amount);
    (bool ok,) = msg.sender.call{value: amount}(""); // external call first
    require(ok);
    balances[msg.sender] -= amount; // state updated AFTER — too late
}

// SAFE: Checks-Effects-Interactions pattern
function withdraw(uint amount) external {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount; // state updated FIRST
    (bool ok,) = msg.sender.call{value: amount}("");
    require(ok);
}

Prevention: Always follow the Checks-Effects-Interactions (CEI) pattern. Update all state variables before any external calls. Additionally, use OpenZeppelin's ReentrancyGuard modifier as a second layer of defense.

#2 — Broken Access Control

Severity: Critical | The #1 vulnerability by total funds lost in 2024.

Functions that should be restricted to the contract owner or specific roles are left unprotected — or protected with flawed logic that can be bypassed.

// VULNERABLE: anyone can call mint
function mint(address to, uint amount) external {
    _mint(to, amount);
}

// SAFE
function mint(address to, uint amount) external onlyOwner {
    _mint(to, amount);
}

More subtle access control bugs include: using tx.origin instead of msg.sender for authentication (can be bypassed via phishing contracts), or missing role checks on initializer functions in upgradeable contracts.

#3 — Integer Overflow and Underflow

Severity: High | Largely mitigated in Solidity 0.8+, but still common in older contracts and assembly blocks.

Before Solidity 0.8, arithmetic operations would silently wrap around. A balance of 0 minus 1 would become 2²⁵⁶-1 — maximum possible value.

// VULNERABLE (Solidity < 0.8)
uint8 x = 255;
x += 1; // wraps to 0

// In Solidity 0.8+, this reverts automatically.
// But in unchecked{} blocks, overflow is re-enabled:
unchecked {
    uint8 y = 255;
    y += 1; // wraps to 0 — no revert!
}

Prevention: Use Solidity 0.8+ and avoid unchecked blocks unless you've formally proven the operation cannot overflow. Use SafeMath for any legacy 0.7.x code.

#4 — Oracle Manipulation

Severity: Critical | Flash loan-amplified oracle attacks have stolen over $500M.

Contracts that use on-chain price sources — particularly AMM spot prices — can be manipulated within a single transaction using flash loans to artificially move the price, exploit the contract, then restore the price.

Red Flag Pattern If your contract reads price from getReserves() on a Uniswap pair directly and uses it to calculate collateral values, liquidation thresholds, or mint quantities — it is vulnerable to oracle manipulation.

Prevention: Use time-weighted average prices (TWAPs) from Uniswap v3, Chainlink price feeds, or Band Protocol. Never trust spot prices from AMMs for anything financially significant.

#5 — Unchecked Return Values

Severity: High | Commonly missed in code reviews because the code looks correct.

Low-level calls (call, delegatecall, send) return a boolean indicating success or failure. Ignoring this return value means a failed transfer is treated as successful.

// VULNERABLE: send() return value ignored
function pay(address payable recipient) external {
    recipient.send(1 ether); // if this fails, execution continues!
}

// SAFE: check the return value
(bool success,) = recipient.call{value: 1 ether}("");
require(success, "Transfer failed");

Similarly, some ERC-20 tokens (notably USDT on mainnet) do not return a boolean from transfer(). Use OpenZeppelin's SafeERC20 library to handle non-standard tokens.

#6 — Flash Loan Attack Vectors

Severity: Critical | Amplifies almost every other vulnerability class.

Flash loans allow borrowing unlimited funds with zero collateral — as long as the loan is repaid in the same transaction. Attackers use them to amplify oracle attacks, governance attacks, and collateral manipulation.

Flash loans themselves aren't a vulnerability — but any contract that makes security decisions based on state that can be temporarily manipulated within one transaction is at risk.

Prevention: Design contracts so that critical state cannot be meaningfully changed and exploited within a single transaction. Use commit-reveal schemes, time delays, and TWAP oracles.

#7 — Front-Running and MEV

Severity: Medium–High | Affects DEXes, NFT mints, and any first-come-first-served mechanism.

Miners (and MEV bots) can see pending transactions in the mempool and insert their own transactions ahead of them — or manipulate transaction ordering. This allows them to sandwich trades, snipe NFT mints, and extract arbitrage.

// VULNERABLE: naïve DEX with predictable slippage
function swap(uint amountIn) external {
    uint price = getSpotPrice(); // bot reads this before tx lands
    // bot sandwiches: buy → your tx (higher price) → sell
}

Prevention: For DEXes, add slippage tolerance parameters that users set explicitly. For competitive auctions, use commit-reveal schemes. For sensitive operations, consider using Flashbots Protect RPC.

#8 — Improper Initialization

Severity: Critical | Unique to upgradeable contracts (proxy pattern).

Upgradeable contracts use an initialize() function instead of a constructor. If this function is not protected with an initializer modifier, anyone can call it after deployment and take ownership.

// VULNERABLE: unprotected initializer
function initialize(address _owner) public {
    owner = _owner; // anyone can call this!
}

// SAFE: use OpenZeppelin's Initializable
function initialize(address _owner) public initializer {
    owner = _owner;
}

Additionally, implementation contracts (the logic behind a proxy) must be initialized immediately at deployment — otherwise attackers can initialize them and use delegatecall to destroy the proxy.

#9 — Denial of Service (DoS)

Severity: Medium–High | Can permanently freeze funds or break contract mechanics.

Several patterns can cause a contract to become permanently non-functional:

// VULNERABLE: loop over user array
function distributeRewards() external {
    for (uint i = 0; i < users.length; i++) { // will fail if users grows large
        payable(users[i]).transfer(rewards[users[i]]);
        // if any transfer fails, entire distribution reverts
    }
}

Prevention: Use pull payment patterns (let users withdraw their own funds) instead of push payments. Avoid unbounded loops over user-controlled data structures.

#10 — Timestamp Manipulation

Severity: Low–Medium | Relevant for time-locked mechanisms.

Validators (formerly miners) have limited ability to manipulate block.timestamp — typically within a 15-second window. Contracts that use timestamps for randomness sources or fine-grained time locks are vulnerable.

// VULNERABLE: timestamp as randomness source
function getWinner() external view returns (address) {
    uint random = uint(keccak256(abi.encode(block.timestamp)));
    return participants[random % participants.length]; // manipulable
}

Prevention: Never use block.timestamp or block.number as a randomness source. Use Chainlink VRF for verifiable randomness. For time locks, a 15-second tolerance window is acceptable for most use cases.

Quick Reference: Risk Matrix

#VulnerabilitySeverityFunds at Risk
1ReentrancyCriticalAll contract ETH/tokens
2Broken Access ControlCriticalAll funds + admin rights
3Integer OverflowHighBalance manipulation
4Oracle ManipulationCriticalAll collateral/liquidity
5Unchecked Return ValuesHighSilent fund loss
6Flash Loan VectorsCriticalAmplified losses
7Front-Running / MEVMedium–HighUser trade value
8Improper InitializationCriticalFull ownership takeover
9Denial of ServiceMedium–HighFrozen funds
10Timestamp ManipulationLow–MediumLottery/randomness bias

How to Check Your Contract for These Issues

The fastest way to identify which of these 10 vulnerabilities exist in your contract is an automated scan. Quantum Audit checks all of the above patterns — plus dozens more — and delivers a risk score from 0–100 with a full PDF report in under 60 seconds.