The most common smart contract vulnerabilities are reentrancy, access control flaws, oracle and price manipulation, front-running, integer and rounding errors, unchecked external calls, upgradeability mistakes, denial of service, signature replay and weak key management. Most of them do not come from exotic cryptography but from ordinary logic that behaves differently once anyone, including an attacker, can call it with any input and in any order. This guide explains each one conceptually, with its typical impact and the practices that prevent it.
The ten vulnerabilities at a glance
Each vulnerability below belongs to a family of mistakes that auditors look for systematically. The table summarizes them before the detailed sections.
| Vulnerability | What goes wrong | Typical impact |
|---|---|---|
| Reentrancy | An external call re-enters the contract before its state is updated | Funds withdrawn several times |
| Access control flaws | A sensitive function can be called by the wrong account | Takeover of the contract or its funds |
| Oracle and price manipulation | The contract trusts a price that can be moved within a transaction | Loans or swaps at a false price |
| Front-running and MEV | Others see and reorder pending transactions | Users receive worse prices or lose opportunities |
| Integer and rounding issues | Arithmetic truncates, overflows or rounds in the wrong direction | Slow leakage of value or broken accounting |
| Unchecked external calls | A failed call is treated as a success | State and balances drift apart |
| Upgradeability and initialization | A proxy or initializer is misconfigured | Takeover or permanent loss of the contract |
| Denial of service | A function can be made to fail for everyone | Funds or features locked |
| Signature replay | A valid signature is accepted more than once or elsewhere | Repeated or unauthorized actions |
| Key management and centralization | Too much power rests on one key or one party | Total loss if the key is compromised or misused |
The OWASP Smart Contract Top 10 offers a complementary classification that many teams use as a checklist.
Logic and state vulnerabilities
Logic and state vulnerabilities arise when the contract's internal accounting can become inconsistent with reality, usually around external calls and arithmetic.
Reentrancy
Reentrancy occurs when a contract calls an external address before it has finished updating its own state, and that address calls back into the contract. The second call sees the old state, for example a balance that has not yet been reduced.
- Typical impact: the same funds are withdrawn repeatedly until the contract is drained. Variants exist across several functions or several contracts that share state, and through read-only functions that return stale values to other protocols.
- Prevention: follow the checks-effects-interactions pattern, add a reentrancy guard on functions that move value, and treat every external call, including token transfers with callbacks, as a point where control leaves your contract.
A minimal illustration of the safe order:
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance"); // checks
balances[msg.sender] -= amount; // effects
(bool ok, ) = msg.sender.call{value: amount}(""); // interactions
require(ok, "Transfer failed");
}
Integer and rounding issues
Integer issues occur when arithmetic produces a result the developer did not expect. Recent Solidity versions revert on overflow by default, but explicitly unchecked blocks, type conversions, older compilers and other languages still expose the risk. Rounding is the more frequent problem today: integer division always truncates, and the direction of rounding decides who absorbs the difference.
- Typical impact: small errors repeated many times, share prices that can be inflated in an empty vault, or accounting that no longer sums to the real balance.
- Prevention: round in favor of the protocol, multiply before dividing, document the precision of every value, and test invariants such as "total shares always match total assets" with fuzzing.
Unchecked external calls
An unchecked external call is one whose failure is silently ignored. Low-level calls return a success flag rather than reverting, and some tokens return false instead of reverting on a failed transfer.
- Typical impact: the contract records a payment or transfer that never happened, and its state diverges from actual balances.
- Prevention: always check return values, use well-reviewed safe transfer wrappers for tokens, and decide explicitly what the contract should do when a call fails.
Permission and governance vulnerabilities
Permission vulnerabilities arise when the wrong party can perform a sensitive action, or when the right party holds more power than users can safely accept.
Access control flaws
Access control flaws occur when a function that should be restricted, such as minting, pausing, changing parameters or withdrawing fees, lacks a check or checks the wrong condition. Common causes include a forgotten modifier, a public initializer, or reliance on the transaction origin instead of the direct caller.
- Typical impact: an attacker mints tokens, changes critical parameters or takes ownership of the contract.
- Prevention: define a written map of roles and the functions each one can call, use established role management libraries, test every restricted function from an unauthorized account, and keep the number of privileged functions small.
Upgradeability and initialization mistakes
Upgradeable contracts separate storage (the proxy) from logic (the implementation), which introduces new failure modes. An upgradeable proxy can suffer from storage layout collisions between versions, an implementation left uninitialized that anyone can claim, or an upgrade function without proper restriction.
- Typical impact: takeover of the contract, corrupted storage after an upgrade, or an implementation destroyed so that the proxy stops working.
- Prevention: use a standard proxy pattern, initialize in the same transaction as deployment, disable initializers on the implementation, check storage layout between versions with tooling, and place upgrades behind a multisig and a timelock.
Key management and centralization risks
Centralization risk exists when one key or one small group can move funds, change rules or upgrade code without delay. It is not a coding bug, but auditors report it because users bear its consequences.
- Typical impact: complete loss if an administrator key is stolen, phished or misused, and a loss of trust even when nothing goes wrong.
- Prevention: use a multisig with independent signers, add a timelock on sensitive changes so users can react, store keys in hardware devices, document who holds which power, and plan how privileges will be reduced over time.
Market and ordering vulnerabilities
Market vulnerabilities arise because blockchain transactions are public before execution and prices on chain can be moved by anyone with enough capital.
Oracle and price manipulation
An oracle supplies external data, most often prices. If a contract reads a price directly from a single liquidity pool, anyone can move that price within one transaction. A flash loan makes this cheap: it provides large capital that must be repaid in the same transaction, so the attacker only needs to pay fees.
- Typical impact: borrowing against inflated collateral, liquidating healthy positions or swapping at a false price.
- Prevention: use decentralized oracles or time-weighted average prices rather than spot prices, check the freshness and plausibility of each value, and define how the protocol behaves when the oracle fails.
Front-running and MEV
Front-running happens when someone sees a pending transaction and places their own before it. Maximal extractable value (MEV) is the broader value that block producers and searchers can capture by ordering, inserting or excluding transactions.
- Typical impact: users receive worse execution on trades, sandwiched swaps, or lose the benefit of actions such as claiming a reward or revealing a bid.
- Prevention: let users set slippage limits and deadlines, use commit-reveal schemes where secrecy matters, avoid designs where the first caller captures value, and consider private transaction submission for sensitive operations.
Availability and authentication vulnerabilities
These vulnerabilities do not always steal funds directly, but they can freeze a protocol or allow actions nobody intended to authorize.
Denial of service
A denial of service makes a function fail for all users. Classic causes are loops over lists that grow without limit until they exceed the block gas limit, and payments pushed to many recipients where one failing recipient blocks everyone.
- Typical impact: withdrawals, liquidations or governance become impossible, sometimes permanently.
- Prevention: bound the size of loops, let users withdraw their own funds instead of pushing payments to them, and make sure no single external party can block a critical path.
Signature replay
Signature replay occurs when a signed message is accepted more than once, on another contract or on another chain. It affects permits, meta-transactions and off-chain order books.
- Typical impact: an approved action is executed repeatedly, or reused where the signer never intended.
- Prevention: include a nonce, an expiry, the contract address and the chain identifier in every signed message, follow the typed data signing standard, and mark each signature as used.
How to reduce these risks in practice
Reducing these risks takes several layers, because no single technique catches every class of vulnerability. A sound process combines:
- Design reviews before coding, to decide roles, oracle sources and upgrade policy deliberately.
- Tests that cover restricted functions, failure paths and edge values, plus fuzzing and invariant testing for accounting.
- Static analysis in continuous integration to catch known patterns early.
- An independent audit of a frozen commit, followed by a re-audit of the fixes. The audit process guide describes each step.
- A bug bounty and monitoring after launch, since new integrations and market conditions create new risks.
Several of these vulnerabilities also drive audit effort. In the site's indicative model, external integrations and upgradeable contracts each add 10% and custom math adds 20% to the estimated auditor-days. The audit cost calculator shows how these factors combine for your codebase; real quotes depend on the actual scope.
An audit reduces risk but does not guarantee the absence of bugs. Treat security as an ongoing discipline rather than a certificate.
Key takeaways
- Most common vulnerabilities come from ordinary logic exposed to hostile callers: call order, arithmetic, permissions and trusted data.
- Checks-effects-interactions, checked return values and bounded loops prevent a large share of logic and availability issues.
- Spot prices, unprotected initializers and single administrator keys are among the design choices auditors question first.
- Combine tests, fuzzing, static analysis, an independent audit, a re-audit and a bug bounty, because each catches different issues.
- No audit guarantees bug-free code; it reduces risk on a precise, frozen scope.