DEV Community

Cover image for Smart Contract Vulnerabilities (2016–2026): 18 Critical DeFi Security Risks
Ankita Virani
Ankita Virani

Posted on

Smart Contract Vulnerabilities (2016–2026): 18 Critical DeFi Security Risks

Smart contracts power decentralized exchanges, lending protocols, DAOs, NFT systems, and cross-chain infrastructure. Today, billions of dollars are controlled by immutable programs deployed on public blockchains.

This architecture brings powerful benefits: transparency, automation, and trustless execution. But it also introduces a unique risk model. Once a smart contract is deployed, its logic is extremely difficult to modify. If a vulnerability exists in the code or economic design, attackers can exploit it immediately.

Over the last decade, the Web3 ecosystem has experienced numerous security incidents. One of the earliest and most influential was the The DAO Hack, where a reentrancy bug allowed an attacker to drain millions of dollars from a decentralized investment fund.

Since then, the DeFi ecosystem has learned hard lessons about smart contract security.

This article provides a comprehensive guide to 18 major smart contract vulnerabilities in Ethereum and DeFi, explaining how these Web3 security risks occur, how attackers exploit them, and how developers can prevent them when building decentralized application

1. Reentrancy Attacks

Reentrancy is one of the most famous vulnerabilities in Ethereum smart contracts. It occurs when a contract performs an external call before updating its internal state.

Because Ethereum allows external contracts to execute arbitrary code when receiving ETH or when invoked through a call, a malicious contract can re-enter the vulnerable function before the original execution finishes.

This allows attackers to repeatedly trigger the same function and manipulate balances.

How Reentrancy Happens

Execution flow:

User calls withdraw()
Contract sends ETH
Receiver fallback executes
Fallback calls withdraw() again
State not yet updated
Funds drained repeatedly
Enter fullscreen mode Exit fullscreen mode

Vulnerable Code Example

mapping(address => uint256) public balances;

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);

    (bool success,) = msg.sender.call{value: amount}("");
    require(success);

    balances[msg.sender] -= amount;
}
Enter fullscreen mode Exit fullscreen mode

Because the contract transfers ETH before updating the balance, the attacker can repeatedly call the function.

Mitigation

Developers follow the Checks-Effects-Interactions pattern:

1 Validate inputs
2 Update internal state
3 Perform external calls
Enter fullscreen mode Exit fullscreen mode

Secure implementation:

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);

    balances[msg.sender] -= amount;

    (bool success,) = msg.sender.call{value: amount}("");
    require(success);
}
Enter fullscreen mode Exit fullscreen mode

Security libraries from OpenZeppelin also provide ReentrancyGuard to prevent recursive calls.

2. Integer Overflow and Underflow

Integer overflow occurs when arithmetic operations exceed the maximum value of a variable type. Underflow occurs when values drop below the minimum value.

Before Solidity 0.8, arithmetic operations wrapped around automatically.

Example:

uint8 x = 255;
x = x + 1;
Enter fullscreen mode Exit fullscreen mode

Result:

0
Enter fullscreen mode Exit fullscreen mode

Similarly:

uint8 x = 0;
x = x - 1;
Enter fullscreen mode Exit fullscreen mode

Result:

255
Enter fullscreen mode Exit fullscreen mode

Attackers exploited these behaviors to manipulate token balances.

Mitigation

Modern Solidity versions revert transactions on overflow.

Previously, developers used SafeMath from OpenZeppelin.

However, overflow can still occur if developers use:

unchecked {
    counter++;
}
Enter fullscreen mode Exit fullscreen mode

which disables safety checks.

3. Access Control Vulnerabilities

Access control vulnerabilities occur when privileged operations are accessible to unauthorized users.

Example:

function mint(address user, uint256 amount) public {
    balances[user] += amount;
}
Enter fullscreen mode Exit fullscreen mode

Anyone can mint tokens, potentially destroying the protocol’s economic integrity.

Sensitive Functions Developers Must Protect

mint
burn
upgrade
setOracle
pause
withdrawTreasury
Enter fullscreen mode Exit fullscreen mode

Mitigation

Common approaches include:

Ownership pattern

modifier onlyOwner() {
    require(msg.sender == owner);
    _;
}
Enter fullscreen mode Exit fullscreen mode

Role-based permissions using AccessControl.

Multisignature governance using secure wallets.

4. Front-Running and MEV Exploits

Front-running occurs when attackers observe pending transactions in the public mempool and submit competing transactions with higher gas fees.

This behavior is part of Maximal Extractable Value (MEV).

DEX platforms like Uniswap are particularly vulnerable.

Sandwich Attack

Transaction order:

attacker buy
victim swap
attacker sell
Enter fullscreen mode Exit fullscreen mode

The attacker profits from the price change caused by the victim’s trade.

Mitigation

Developers use:

slippage limits
batch auctions
private transaction relays (Flashbots)

5. Flash Loan Attacks

Flash loans allow users to borrow assets without collateral if the loan is repaid within the same transaction.

This feature was popularized by Aave.

Flash loans dramatically changed the threat model because attackers can temporarily control massive liquidity.

Typical attack flow:

borrow flash loan
manipulate price
exploit protocol logic
repay loan
Enter fullscreen mode Exit fullscreen mode

Examples include the bZx Flash Loan Attacks and Harvest Finance Exploit.

6. Oracle Manipulation

DeFi protocols rely on price feeds to determine asset value, collateral requirements, and liquidation thresholds.

If attackers manipulate the price source, the protocol’s economic logic can break.

Example calculation:

price = reserveA / reserveB
Enter fullscreen mode Exit fullscreen mode

If the liquidity pool is small, attackers can manipulate this ratio.

Attack flow

1 attacker takes a flash loan
2 attacker buys large amount of token in low liquidity pool
3 price spikes artificially
4 protocol reads manipulated price
5 attacker borrows assets using inflated collateral
6 attacker repays flash loan
7 attacker keeps borrowed funds
Enter fullscreen mode Exit fullscreen mode

Examples include:

Mango Markets Exploit
Cream Finance Exploit

Mitigation

Developers use:

Time-Weighted Average Price (TWAP)
multiple oracle sources
price deviation limits

Reliable oracle networks include:

Chainlink
Pyth Network

7. Delegatecall Vulnerabilities

delegatecall is an EVM instruction that allows a contract to execute code from another contract while using the storage of the calling contract.

This feature is widely used in upgradeable proxy patterns.

When a proxy contract receives a transaction, it forwards execution to an implementation contract using delegatecall.

delegatecall behavior

code executed → implementation contract
storage used → proxy contract
msg.sender preserved
Enter fullscreen mode Exit fullscreen mode

This means the implementation contract can modify the proxy’s storage.

Why This Is Dangerous

If the implementation address is not carefully controlled, attackers could redirect the proxy to a malicious contract.

Example vulnerable pattern:

delegatecall(target)
Enter fullscreen mode Exit fullscreen mode

If target is user-controlled, an attacker can overwrite important variables such as:

owner
admin
implementation
Enter fullscreen mode Exit fullscreen mode

This would give the attacker full control over the contract.

Mitigation

Secure proxy implementations include:

• restricting implementation addresses
• validating upgrades
• using audited proxy frameworks

Libraries from OpenZeppelin provide widely used upgradeable proxy contracts.

8. Uninitialized Contract Storage

Upgradeable contracts cannot use constructors because the proxy deploys the logic contract separately.

Instead, they use initializer functions.

Example:

function initialize() public {
    owner = msg.sender;
}
Enter fullscreen mode Exit fullscreen mode

If the initialization function is not protected, anyone can call it.

This allows attackers to become the owner of the contract.

Real World Example

The Parity Multisig Wallet Hack occurred because the wallet library contract was not properly initialized.

An attacker called the initialization function and gained ownership of the library contract. The attacker then executed selfdestruct, permanently breaking all dependent wallets.

Millions of dollars worth of ETH became inaccessible.

Mitigation

Use initialization guards.

Example:

initializer
Enter fullscreen mode Exit fullscreen mode

from OpenZeppelin upgradeable libraries.

Also ensure initialization occurs immediately after deployment.

9. Denial of Service (DoS)

Denial-of-service vulnerabilities occur when attackers make contract functions unusable.

These attacks do not necessarily steal funds but can disrupt protocol functionality.

Gas Limit DoS

Example:

for(uint i = 0; i < users.length; i++)
Enter fullscreen mode Exit fullscreen mode

If the array grows too large, the function may exceed the block gas limit.

Attackers can intentionally increase the array size to make the function impossible to execute.

Forced Revert DoS

Example:

require(receiver.send(value))
Enter fullscreen mode Exit fullscreen mode

If the receiver contract intentionally reverts, the entire transaction fails.

Mitigation

Best practices include:

• batching large operations
• using pull-based payment models
• avoiding unbounded loops

10. Timestamp Manipulation

Block timestamps are not perfectly reliable.

Validators can slightly adjust timestamps within a limited range.

If contracts rely on timestamps for important logic, attackers or validators can manipulate outcomes.

Example:

if(block.timestamp % 2 == 0)
Enter fullscreen mode Exit fullscreen mode

This kind of logic is unsafe.

Why Timestamp Manipulation Matters

Consider a lottery contract that determines winners using timestamps.

Validators could adjust timestamps to influence the result.

Mitigation

Avoid using timestamps for randomness or critical financial logic.

Instead use secure randomness sources.

11. Weak Randomness

Randomness is difficult in blockchain environments because all nodes must produce deterministic results.

Many developers attempt randomness using values such as:

block.timestamp
block.number
blockhash
Enter fullscreen mode Exit fullscreen mode

These values are predictable or miner-influenced.

Attackers can often compute outcomes in advance.

Secure Randomness

Protocols use verifiable random functions (VRF) provided by oracle networks.

Example providers include Chainlink.

VRF systems generate randomness that is provably unbiased and verifiable.

12. Logic Errors in Protocol Design

Some of the most dangerous vulnerabilities occur even when the code itself is technically correct.

The issue lies in flawed economic assumptions.

These vulnerabilities often appear in lending protocols, derivatives systems, and complex DeFi primitives.

Example Case

The Euler Finance Hack exploited complex lending mechanics involving liquidation logic and internal accounting.

Attackers combined flash loans and protocol interactions to manipulate internal balances and drain funds.

Why These Bugs Are Hard to Detect

Traditional static analysis tools cannot detect economic vulnerabilities.

Detection requires:

• adversarial simulations
• economic modeling
• invariant testing

13. Cross-Chain Bridge Vulnerabilities

Cross-chain bridges allow assets to move between different blockchains.

They represent one of the largest attack surfaces in Web3.

Typical bridge architecture:

lock token on chain A
mint wrapped token on chain B
Enter fullscreen mode Exit fullscreen mode

The bridge must verify that a lock event occurred on the source chain.

Major Bridge Exploits

Examples include:

Ronin Network Hack
Wormhole Bridge Hack
Nomad Bridge Hack

These incidents resulted in hundreds of millions of dollars in losses.

Common Bridge Vulnerabilities

• validator key compromise
• message replay attacks
• incorrect signature verification

Because bridges hold massive liquidity, they are frequent attack targets.

14. Storage Collision in Upgradeable Contracts

Upgradeable proxy architectures separate contract logic from storage.

If the storage layout changes during upgrades, variables can overwrite each other.

Example storage layout:

slot 0 owner
slot 1 balance
Enter fullscreen mode Exit fullscreen mode

If a new implementation modifies the order:

slot 0 balance
slot 1 owner
Enter fullscreen mode Exit fullscreen mode

Data corruption occurs.

Mitigation

Maintain consistent storage layout across upgrades.

Developers often use storage gap patterns:

uint256[50] private __gap;
Enter fullscreen mode Exit fullscreen mode

This reserves future storage space.

15. Signature Replay Attacks

Many protocols use off-chain signatures for gasless transactions.

Example process:

user signs message
contract verifies signature
action executed
Enter fullscreen mode Exit fullscreen mode

If signatures lack uniqueness, attackers can reuse them.

Replay Attack Example

user signs withdrawal
attacker submits signature twice
contract executes withdrawal twice
Enter fullscreen mode Exit fullscreen mode

Mitigation

Include unique identifiers in signatures:

nonce
chainId
expiration timestamp
Enter fullscreen mode Exit fullscreen mode

Typed structured data signatures such as EIP-712 also improve security.

16. Selfdestruct Vulnerabilities

The selfdestruct opcode permanently removes a contract and sends remaining ETH to another address.

Older systems relied on external contracts that could be destroyed.

If a dependency self-destructs, the calling protocol may break.

Ethereum protocol updates such as EIP-6780 reduced some risks, but legacy contracts may still be affected.

17. Gas Griefing

Gas griefing attacks attempt to make transactions fail by consuming excessive gas.

Attackers can exploit patterns such as:

• expensive loops
• fallback functions
• large return data

Example:

return massive array
Enter fullscreen mode Exit fullscreen mode

This increases gas costs and may cause transactions to revert.

Mitigation

Developers should avoid operations dependent on user-controlled data sizes and ensure gas costs remain bounded.

18. Upgradeability Risks

Upgradeable contracts provide flexibility but introduce governance risks.

If upgrade permissions are compromised, attackers can deploy malicious implementations.

Example attack scenario:

attacker gains admin key
attacker upgrades contract
malicious code drains funds
Enter fullscreen mode Exit fullscreen mode

Mitigation

Secure upgrade systems include:

• multisignature wallets
• timelocked upgrades
• decentralized governance

These mechanisms reduce the risk of a single compromised key controlling protocol upgrades.

Final Lessons for Smart Contract Developers

The history of DeFi exploits shows a clear pattern.

Early attacks exploited simple coding mistakes. Modern attacks focus on economic design flaws, oracle manipulation, and cross-chain infrastructure weaknesses.

Secure smart contract development requires:

defensive coding
thorough auditing
economic modeling
continuous monitoring

Security must be treated as a core architectural requirement, not an afterthought.

As DeFi protocols become more complex, the attack surface continues to expand. Modern exploits rarely rely on simple Solidity bugs. Instead, attackers combine economic manipulation, oracle dependencies, governance weaknesses, and cross-protocol interactions.

For developers building the next generation of Web3 infrastructure, security must be integrated at every layer of protocol design. Audits, invariant testing, economic simulations, and continuous monitoring are no longer optional. They are fundamental requirements for building secure decentralized systems.

Top comments (0)