Smart Contract Vulnerabilities List: 22 to Check First
Quick answer. A practical smart contract vulnerabilities list for a first pass has 22 attack vectors. They run from front-running, zero inputs and weird ERC-20 tokens through price manipulation and reentrancy to selector clashing, signatures and hash collisions.
This smart contract vulnerabilities list is the toolbox to reach for when you open a fresh codebase and want a real finding within the first hour.
It suits an initial review, early notes for a client before a full engagement, or a sweep across many audit contests at once.
Where another Resource Hub post covers an item in depth, the item links to it.
The 22 at a Glance
| # | Attack vector | Go deeper |
|---|---|---|
| 1 | Front-running and back-running | Front-running |
| 2 | Very small amounts | Rounding errors |
| 3 | Passing zero as an input | |
| 4 | Contracts that cannot accept ether | Sneaky Solidity bugs |
| 5 | Gas griefing with external calls | External call attacks |
| 6 | Weird ERC-20 tokens | Weird ERC-20 tokens |
| 7 | Price manipulation | Oracle manipulation |
| 8 | Blacklisted ERC-20 addresses | Liquidation vulnerabilities |
| 9 | Overflow and underflow | Sneaky Solidity bugs |
| 10 | Block reorgs | |
| 11 | Reentrancy | Reentrancy attacks |
| 12 | Sybil attacks on incentives | Reward accounting |
| 13 | Flash loans | Oracle manipulation |
| 14 | Accepting data from any address | Sneaky Solidity bugs |
| 15 | Inflating accounting with direct transfers | ERC-4626 inflation attack |
| 16 | Forced precision loss | Rounding errors |
| 17 | Addresses that gain code later | Metamorphic contracts |
| 18 | Reverting | External call attacks |
| 19 | Unexpected addresses | |
| 20 | Selector clashing | Upgradeable contracts |
| 21 | Signatures | Signature malleability |
| 22 | Hash collisions |
Inputs, Calls and Tokens
1. Front-Running and Back-Running
Developers design a system for the way they expect it to be used. Front-running and back-running are not part of that picture, yet every attacker has them.
These findings usually involve time dependence. Can a bid be overwritten, then removed?
In one marketplace audit, an attacker could back-run a new bid with a higher bid, then remove that higher bid. The result was no bid at all, so bids could be erased.
The attacker could also front-run the owner accepting a bid and erase it. The owner could never sell the NFT for a bid that was really placed.
Unaudited code often has a scenario like this. For more, see front-running attacks on smart contracts.
2. Using Very Small Amounts
Tiny amounts feed rounding and precision loss. A single wei divided by something will often round down to zero.
Test whether one wei can push the protocol into an invalid state.
A stateful fuzzing suite covers small amounts well, as long as its invariants are good. See rounding errors in Solidity.
3. Passing Zero as an Input
If zero is not an expected input, disallow it explicitly. That cuts off even the potential for something unexpected.
This covers zero addresses, zero uint values, empty lists and lists of zero values. Developers build for the spec, not the malicious case, and a zero input can push the system into a state nobody planned for.
4. Contracts That Cannot Accept Ether
Think like the attacker. Is the system sending ether to an address the user chooses?
Two-step systems are the classic case: a keeper executes someone's order on an exchange and has to send ether as part of execution. Put a contract that rejects ether at that address, and the send fails.
The result is denial of service. Only a forced send, such as the force-send helper in a library like Solady, avoids it, and most protocols do not use one.
5. Gas Griefing With External Calls
Watch for external calls to an untrusted, arbitrary address that the transaction initiator does not control. That address can burn a huge amount of gas, and the initiator pays for it.
Untrusted calls need a gas limit, ideally configurable rather than hardcoded. If there is none, you may have an easy gas griefing finding. See the four external call attacks.
6. Weird ERC-20 Tokens
ERC-777 tokens with callbacks are the classic source of easy findings. Fee-on-transfer tokens are another.
Then there are tokens like USDT, with unusual return values and a rule that an approval must reset to zero before a new nonzero value.
By early 2024, automated bot races in audit contests were catching much of this low-hanging fruit.
Blacklists get their own item below. The full set of behaviors is in the weird ERC-20 tokens checklist.
7. Price Manipulation
A contract that reads the reserves of a liquidity pool, such as a Uniswap pool, or reads slot0 from a Uniswap V3 pool, is an immediate red flag. Using raw reserves is almost always a finding.
The more interesting cases are tokens priced from an exchange rate, such as vault tokens. Those rates can also be manipulable onchain.
Take careful note whenever a token's price can be moved, and dig deeper.
More often than not, that path leads to a high or critical exploit. See oracle manipulation attacks.
8. Blacklisted ERC-20 Addresses
USDC and USDT keep a blacklist mapping. A blacklisted address cannot use the token.
This is a favorite in any protocol that needs liquidations to go through: lending and borrowing, perpetuals, options. First ask whether the liquidation transfers tokens to an arbitrary address or to the position owner.
Then ask whether that transfer can be in USDC, USDT or another blacklistable token. If both are true, a transfer to a blacklisted owner makes the liquidation revert.
The hard part is getting the blacklistable token in as collateral for a position a blacklisted account owns. Deposit-for functions, or two-step flows that attribute funds to an owner, usually make it possible.
This finding repeats across protocols because almost nobody thinks about blacklisted tokens. The liquidation side is covered in liquidation vulnerabilities.
Math, Blocks and Accounting
9. Overflow and Underflow
Solidity 0.8 reverts with a panic when addition or subtraction overflows. Casting is where overflow still hides.
Look hardest at protocols packing storage slots with uint128 or uint64. If you can make a downcast from uint256 overflow, you very likely have a critical finding.
The sneaky Solidity bugs post covers downcasting alongside other quiet failures.
10. Block Reorgs
Few people talk about this one. Blocks can be reorganized after they are recorded, and some designs break when that happens.
Picture a protocol with a global nonce that only increases, where each pool's address comes from the nonce alone. Alice, an honest user, creates a pool at nonce zero.
Carrie and Allan trust Alice and send funds to the nonce-zero pool address. Bob sees a reorg coming and gets his own pool creation recorded before Alice's.
After the reorg, Bob owns the nonce-zero pool with Carrie and Allan's funds in it.
Alice's pool sits at nonce one, empty, and Bob can pull the funds out through whatever logic the pool has.
This only applies to specific protocol designs, but it is a real finding when it does.
11. Reentrancy
Look at the external calls and the external functions available, check for missing nonReentrant modifiers, and look for ways to reenter across contracts. Every type is covered in reentrancy attacks explained.
12. Sybil Attacks on Incentives
This one is usually obvious in reward code. Can you split funds across many accounts and earn more than you would from one account?
Combine it with front-running for a second angle. Front-run a reward compound by depositing, say, 99% of what the vault will hold.
Everyone who was there before gets 1% of the rewards, and you take 99%. Withdraw right after the compound and walk away with the incentives.
That second version is an offshoot of stepwise jumps, covered in DeFi reward accounting vulnerabilities.
13. Flash Loans
Always consider flash loans, especially in AMMs and concentrated liquidity protocols. Swapping the price down to a certain tick and back often does something interesting.
In these protocols, invariant testing becomes extremely important. Flash loans are the attacker's tool for breaking an invariant, and catastrophic things follow when one breaks.
A typical pattern is using liquidity from a different range while the price sits between two ticks. The KyberSwap exploit, recent at the time of recording, looked something like this.
Flash loans go hand in hand with price manipulation (item 7).
14. Accepting Data From Any Address
A high-level .call to an arbitrary contract copies its return data into memory. That data can be enormous, just padding repeated over and over.
Memory expansion cost grows quadratically, so each extra word costs more gas than the last. A malicious address can make you spend far more gas than you expected.
A gas limit on the call does not help, because copying the return data happens on your side.
Ideally, do not accept return data from an arbitrary external address.
The sneaky Solidity bugs post covers return data handling too.
15. Inflating Accounting With Direct Transfers
Whenever you see balanceOf, get interested. It is the first of three red flags for an inflation attack.
The outcome is not always an inflation attack, but inflating the system's accounting by sending tokens straight to the contract usually leads there.
It is not limited to simple vaults: it can happen in any system.
The three red flags are covered in the ERC-4626 inflation attack guide.
Precision, Addresses and Encoding
16. Forced Precision Loss When It Really Matters
Some protocols carry validations that should always hold. GMX V2, for example, checks that a market's actual ERC-20 balance matches the accounting held in storage.
A check like that is a last line of defense. It is not needed for the system to run, or even to be secure.
Those checks can turn against you. If you can force precision loss so a value lands just under the validation as it is computed, the check fails and reverts.
Now nobody can use the protocol. That denial of service is an exploit in itself, which is ironic for a check meant to prevent exploits.
GMX's implementation is safe here, but many teams get it wrong. Look hard at every last-line-of-defense validation.
17. Addresses That Are Empty, Then Hold Code
An address can have no bytecode at one moment and hold a contract at another. An external call made from inside a constructor is one case.
Metamorphic contracts and creative uses of CREATE2 are others.
Even without them, predicting where your next contract will deploy on a chain is close to trivial, since the address comes from a deterministic formula.
Any system relying on an address having no code is a red flag. List the ways around that check. See metamorphic contracts.
18. Reverting
Look for external calls or inputs you can make revert. These denial of service attacks can be critical. The external call attacks guide goes deep on each path.
19. Unexpected Addresses
Large multi-contract DeFi systems often let you name an arbitrary receiver for a swap, a position increase or decrease, a repayment or a borrow. Try passing another contract in the system as that receiver.
One critical finding in GMX V2, found during its audits before deployment, worked this way. A validation reverted whenever that other contract was the receiver.
Because order execution was a two-step process, the attacker could switch between two outcomes. With funds going to that receiver, execution reverted; flip the switch so no funds went there, and the trade executed.
Execution used prices from the block in which the market order was submitted. Choosing whether a trade went through gave a risk-free trade.
In big systems, always ask which important addresses a receiver could be.
20. Selector Clashing
Watch for this in upgradeable systems. Say the implementation has a function with selector 0xa (real selectors are four bytes, but keep it short), and the proxy has its own function with the same selector.
A user calling the proxy to reach the implementation never gets there. The call hits the proxy's function instead.
Function selectors, selector clashing and storage collisions are explained in upgradeable smart contracts explained.
21. Signatures
Signatures carry many edge cases: replay on different chains, malleability, where a modified signature recovers to the same address, and ecrecover returning the zero address for an invalid signature.
Raw ecrecover in a contract, instead of OpenZeppelin's ECDSA library, is an absolute red flag. People rarely get it right, short signatures included.
For the background, read Ethereum cryptography and secp256k1, then signature malleability.
22. Hash Collisions
Packing two dynamic types side by side with abi.encodePacked, then hashing, is ambiguous. The packed bytes do not record where one list ends and the next begins.
So two lists of 0, 1, 2 and 3, 4, 5 pack to the same bytes as 0, 1 and 2, 3, 4, 5.
Several combinations of the lists give the same hash. Whenever two dynamic types sit next to each other in abi.encodePacked before hashing, expect a problem.
Keep the List Close
Work a few contests with these 22 vectors in mind and you will find something.
Write them down in a note-taking tool.
Check them on every review, because it is easy to forget that a protocol hashing two lists together is a problem until your notes remind you.
How Guardian Helps
Guardian's smart contract audits start with a design review, then build an invariant suite around solvency, accounting and rounding.
Senior researchers then attack protocol logic, integrations, privileged flows and economic assumptions with proof-of-concept depth.
Frequently Asked Questions
What should I check first in a new smart contract codebase?
Start with these 22 vectors, from front-running, very small amounts and zero inputs through to signatures and hash collisions. Keep the list in your notes and check it on every review, so nothing slips your mind.
Does Solidity 0.8 stop overflow bugs?
Only for arithmetic. Addition and subtraction revert on overflow, but explicit downcasts, such as uint256 to uint128 or uint64, still overflow silently. Protocols that pack storage slots are where to look, and an overflowing downcast is often critical.
Why are blacklisted tokens such a repeatable finding?
USDC and USDT can blacklist addresses. If a liquidation sends a blacklistable token to the position owner, a blacklisted owner makes the liquidation revert. Almost nobody designs for this, so the same finding appears across lending, perpetuals and options protocols.
Why is balanceOf a red flag?
Anyone can send tokens straight to a contract. If accounting reads balanceOf, a direct transfer inflates it, which is the first of three red flags for an inflation attack. It affects any system, not only simple vaults.
Sources
Get your codebase checked against every vector
Guardian's researchers bring this toolbox, and much more, to every audit.