Smart Contract Attack Vectors: Reentrancy, Read-Only Reentrancy & Flash Loan Manipulation
By NorwegianSpark Editorial — written with AI assistance and reviewed by the NorwegianSpark SA editorial team | Last updated: 2026-04-17
This article contains affiliate links. We may earn a commission at no extra cost to you. Full disclosure
The Anatomy of Reentrancy: Single-Function vs. Cross-Function Attacks
Reentrancy was the fatal vulnerability responsible for the historic 2016 The DAO hack (which drained 3.6 million ETH and caused the Ethereum/Ethereum Classic hard fork). Despite being known for a decade, reentrancy variants remain among the most prevalent and costly attack vectors in decentralized finance.
The mechanics of classic Single-Function Reentrancy:
- Vulnerable Execution Order: A smart contract function transfers ETH or tokens to an external address before updating its internal user balance state variable.
- Control Flow Hijack: When sending ETH to a contract address, the EVM invokes the recipient's
receive()orfallback()function, handing execution control to the attacker's smart contract. - Recursive Invocations: The attacker's fallback function immediately calls the vulnerable contract's
withdraw()function a second time. Because the internal balance state has not yet been decremented, the contract checks the original balance, approves the withdrawal, and sends another ETH payout. - Capital Drainage: This recursive loop repeats until the vulnerable contract is completely drained of funds or hits the gas limit, after which all state updates execute against an already empty vault.
Cross-Function Reentrancy:
Cross-function reentrancy occurs when a contract protects function with a reentrancy lock, but shares the same vulnerable state variable with unprotected function . The attacker enters through function , hands execution to fallback, and reenters the contract via function (such as transferBalance() or collateralize()) to exploit the intermediate inconsistent state.
Read-Only Reentrancy: The Curve LP Token Price Oracle Exploit
Traditional reentrancy guards protect state-mutating functions (functions marked external or public that modify storage). However, read-only view functions are almost never protected by reentrancy locks because they do not modify state.
Read-Only Reentrancy occurs when a secondary protocol relies on the view function of a primary protocol as a price oracle while the primary protocol is in the middle of a state transition.
The classic Curve LP Read-Only Reentrancy Attack Vector:
- Primary Pool (Curve / Balancer): A user calls
remove_liquidity()on a Curve pool. - Intermediate State Inconsistency: The Curve contract burns the user's LP tokens and transfers the underlying tokens (e.g., ETH) back to the user before updating its internal virtual price state variable.
- Fallback Trigger: During the ETH transfer, control is handed to the attacker's fallback hook.
- Oracle Exploitation on Secondary Protocol: Inside the fallback hook, the pool has less ETH, but its LP token supply has not updated yet. As a result, calling Curve's
get_virtual_price()returns an artificially depressed or inflated price. - Borrowing / Liquidating: The attacker calls a secondary lending protocol that uses Curve's
get_virtual_price()as a pricing oracle, borrowing massive assets against undervalued collateral or triggering false liquidations before the Curve transaction finishes and restores accurate pricing.
Defense against Read-Only Reentrancy: Secondary protocols must never query raw spot view functions from external pools without verifying that the target pool is not currently locked in an active reentrancy state. Protocols can call reentrancy lock inspection functions or enforce reentrancy guards across view methods using EIP-1153 transient storage.
Flash Loan Price Oracle Manipulation: Why Spot Prices Are Fatal in DeFi
A Flash Loan allows anyone to borrow millions of dollars of cryptocurrency without collateral, provided the borrowed principal plus a tiny fee (e.g., 0.05% on Aave) is repaid within the exact same atomic transaction block.
If a DeFi protocol queries the spot reserves of an Automated Market Maker (such as UniswapV2Pair.getReserves()) to determine asset prices, an attacker can manipulate the oracle risk-free using flash loans.
The Flash Loan Oracle Manipulation Attack Flow:
- Borrow: Attacker borrows $50,000,000 USDC via flash loan.
- Manipulate Spot AMM: Attacker dumps all $50M USDC into a USDC/WETH liquidity pool on Uniswap, causing the spot price of WETH in that pool to skyrocket artificially.
- Exploit Vulnerable Protocol: Attacker deposits 100 WETH as collateral into a vulnerable lending protocol. The protocol calculates collateral value by querying the manipulated Uniswap spot reserve, calculating that the 100 WETH is worth 300,000.
- Over-Borrow: Attacker borrows $8,000,000 in real stablecoins from the lending vault against the fake collateral valuation.
- Rebalance & Repay: Attacker swaps back on Uniswap (recovering most of the dumped USDC) and repays the $50M flash loan, pocketing millions in stolen vault funds.
Why Spot Oracles Fail: Spot prices reflect only the current marginal ratio of tokens in an AMM pool at a single discrete instant. They represent the cost of the last trade, not the global fair market value.
The Solution: Decentralized Oracles (Chainlink) & TWAP: Protocols must use Time-Weighted Average Price (TWAP) oracles (such as Uniswap v3 TWAP over a 30-minute window) or decentralized oracle networks (such as Chainlink Decentralized Oracle Networks with multi-source medianization and outlier rejection), which cannot be manipulated in a single atomic flash-loan transaction.
Case Study: Euler Finance $197M Donation Logic Exploit
In March 2023, Euler Finance suffered a $197 million exploit—one of the largest in DeFi history. The vulnerability was not in external oracle manipulation or standard reentrancy, but in a subtle logic flaw in their donateToReserves() function.
The Root Cause Analysis:
- Collateral and Debt Tracking: Euler used internal liquidity tokens (
eTokens) to represent deposits and debt tokens (dTokens) to represent borrowed liabilities. - The Health Check Invariant: Whenever a user borrowed funds or withdrew collateral, Euler's smart contract executed a health check verifying that .
- The Missing Health Check: In an upgrade (EIP-14), Euler added a
donateToReserves()function allowing users to donate collateral to the protocol reserve. However, the developers omitted the health check insidedonateToReserves(). - The Exploit Execution:
- Attacker leveraged flash loans to deposit 200M in leveraged collateral and debt.
- Attacker called
donateToReserves()to donate their collateral eTokens to the reserve. - Because the donation lacked a health check, the attacker's account was left in an underwater state (enormous debt with zero collateral).
- Liquidating such a severely underwater position triggered a liquidation bonus calculation algorithm that minted massive eTokens to the liquidator (also controlled by the attacker), converting bad debt into withdrawable funds and draining $197M.
The Takeaway: Invariant testing must enforce that every state-mutating function validates global account solvency, regardless of how harmless the function appears.
Arithmetic Precision Loss, Rounding Exploits & First-Depositor ERC-4626 Attacks
In the EVM, integer division truncates toward zero: . If arithmetic calculations in token vaults or AMMs perform division before multiplication, severe precision loss occurs:
The First-Depositor / Inflation Attack on ERC-4626 Tokenized Vaults: In standard ERC-4626 vaults, shares minted for a deposit are calculated as:
If an attacker is the very first depositor in an empty vault:
- Attacker deposits wei of asset and receives wei of share ( initial ratio).
- Attacker front-runs a legitimate user's deposit of USDC by directly transferring USDC directly to the vault contract address (without calling
deposit()). - Now: USDC, .
- When the victim deposits USDC:
- The victim receives shares, but their USDC remains in the vault. The attacker then redeems their single share, withdrawing both their original deposit and the victim's USDC!
Defenses against Inflation Attacks:
- Virtual Shares & Assets: Modern implementations (such as OpenZeppelin ERC4626) add virtual offset shares (e.g., virtual shares) to the denominator, preventing share price manipulation.
- Dead Shares Burning: The vault permanently mints and burns the first shares to the zero address on initialization.
## Access Control Failures: Unprotected Initializers, Signature Replay & tx.origin
Beyond complex algorithmic flaws, access control oversights continue to cause multi-million dollar hacks:
- Unprotected Initializer Functions in Proxies:
Upgradeable implementation contracts use initialize() functions instead of constructors. If the implementation contract itself is left uninitialized on-chain, an attacker can call initialize() directly, become the contract owner, and call self-destruct or malicious delegatecalls (as seen in the Nomad and Wormhole bridge incidents).
Mitigation: Use OpenZeppelin's _disableInitializers() in implementation constructors.
- Signature Replay Attacks:
If a contract verifies an off-chain ECDSA signature for meta-transactions without enforcing a chainId, contract address, and unique per-user nonce, the attacker can reuse the same signed payload across different chains or repeatedly on the same contract.
Mitigation: Enforce EIP-712 structured typed data signatures with domain separators.
- Authentication via
tx.origininstead ofmsg.sender:
Using require(tx.origin == owner) allows phishing attacks: if the owner interacts with a malicious contract, that malicious contract can call the protected vault and pass the authentication check because tx.origin remains the owner's EOA.
Mitigation: Always use msg.sender for authorization.
Frequently asked questions
Why does the Checks-Effects-Interactions (CEI) pattern prevent reentrancy?
By updating internal balances and state variables before making external calls or token transfers, any recursive reentrant call will see the decremented balance and immediately fail the initial checks.
How do virtual shares prevent ERC-4626 vault inflation attacks?
Virtual shares add a fixed non-zero offset to the share calculation denominator, making it mathematically impossible for an attacker to inflate the share price so high that subsequent user deposits round down to zero.
Why should smart contracts avoid using spot prices from Uniswap pools?
Spot prices reflect only the current token balance in a single pool and can be manipulated instantaneously within a single transaction using uncollateralized flash loans.
Related reading
- invariant testing smart contracts against reentrancy attacks — Learn how to detect reentrancy and oracle vulnerabilities automatically using fuzzing tools.
## Sources
- Security Considerations: Reentrancy — Solidity docs
- Echidna: property-based fuzzer for Ethereum smart contracts — Trail of Bits
No contributor to this article holds a professional cryptography or security credential. Every technical claim above is sourced to primary protocol documentation rather than to personal authority — follow the sources and verify anything you intend to act on.
Not financial advice. Crypto assets are volatile and can lose value. This article describes how protocols work, not what you should buy.
Content on AICryptoCoin is for informational purposes only and does not constitute financial advice. Always do your own research and consult a qualified financial advisor before making investment decisions.
Related Articles
Smart Contract Upgradeability Patterns: Transparent, UUPS, Diamond & Beacon Proxies
16 min
Protocol Deep DivesHigh-Frequency DeFi Security: Automated Circuit Breakers & Invariant Monitoring
15 min
Protocol Deep DivesFormal Verification & Invariant Testing: Certora, Echidna & Foundry Fuzzing
17 min