Read-only reentrancy has emerged as one of the most dangerous and persistent vulnerability classes in decentralized finance, responsible for multiple multi-million dollar exploits throughout 2022 and early 2023. The dForce attack on February 10, which resulted in a $3.65 million loss across Arbitrum and Optimism before funds were recovered on February 13, provides an ideal case study for understanding this sophisticated attack vector at a technical level.
The Objective
This tutorial aims to equip experienced smart contract developers and security researchers with a deep understanding of how read-only reentrancy exploits work, why they are particularly insidious, and how to protect protocols against them. By the end of this walkthrough, you should be able to identify read-only reentrancy vectors in code reviews, implement the correct mitigations, and audit existing integrations for exposure to this vulnerability class.
Prerequisites
This tutorial assumes familiarity with Solidity smart contract development, understanding of the Ethereum Virtual Machine execution model, and basic knowledge of DeFi protocols including lending platforms, automated market makers, and oracle systems. You should be comfortable reading Solidity code and understanding concepts like view functions, reentrancy guards, and flash loan mechanics.
For context, you should understand that standard reentrancy involves a malicious contract calling back into the vulnerable contract during state changes, before those changes are fully committed. The classic example is an Ether withdrawal function that sends funds before updating the sender’s balance, allowing repeated withdrawals. Read-only reentrancy is a subtler variant that targets view functions rather than state-changing functions.
Step-by-Step Walkthrough
Step 1: Understand the Curve Virtual Price Oracle
Curve Finance pools expose a get_virtual_price function that returns the virtual price of LP tokens, accounting for accumulated trading fees. Many DeFi protocols, including dForce, use this virtual price as an oracle to value Curve LP tokens deposited as collateral. The virtual price should normally only increase over time as fees accumulate, making it appear safe for use in collateral calculations.
Step 2: Identify the Reentrancy Window
The vulnerability exists in Curve’s remove_liquidity function. When a user removes liquidity from a Curve pool, the contract transfers the underlying tokens to the user before updating the pool’s internal accounting. During this brief window, a callback to the user’s contract can call get_virtual_price, which now returns an incorrect value because the pool state has been partially updated — tokens have been sent but the virtual price calculation has not been adjusted.
This creates a momentary inconsistency where get_virtual_price returns a manipulated value. Any protocol reading this value during the callback window receives incorrect oracle data.
Step 3: Trace the dForce Attack Path
The dForce attacker used flash-loaned funds to deposit into Curve’s wstETH/ETH pool and receive LP tokens. These LP tokens were then deposited into dForce’s wstETHCRV-gauge vault as collateral. The attacker then called remove_liquidity on the Curve pool. During the reentrancy callback in remove_liquidity, the attacker’s contract interacted with dForce’s vault, which called get_virtual_price on the Curve pool. Because the pool state was inconsistent during the callback, the virtual price was artificially manipulated.
This manipulated oracle value allowed the attacker to overvalue their collateral position in dForce, enabling them to borrow more assets than their actual collateral supported. The difference between the borrowed amount and the true collateral value represented the exploit profit — approximately $1.9 million on Arbitrum and $1.7 million on Optimism.
Step 4: Implement the Mitigation
Curve itself recommends a simple mitigation: call any method with the nonreentrant modifier before relying on get_virtual_price. The cheapest approach is removing zero liquidity, which acquires the reentrancy lock without modifying state. Protocols integrating with Curve should wrap their oracle reads in this pattern.
An alternative mitigation is to cache oracle values rather than reading them on-demand. By reading get_virtual_price at the beginning of a transaction and using the cached value throughout, protocols eliminate the window where reentrancy could manipulate the reading.
Troubleshooting
A common mistake when implementing the mitigation is placing the reentrancy guard call in the wrong location. The guard must be called before any code that relies on the virtual price value, not after. Some developers incorrectly add the guard after their collateral calculation, which provides no protection.
Another issue arises with gas optimization. Some protocols skip the zero-liquidity removal to save gas, reasoning that the attack is unlikely. The dForce exploit demonstrates that the gas savings — typically a few thousand wei — are utterly disproportionate to the potential losses from a successful attack. Always prioritize security over gas optimization when dealing with oracle integrations.
Testing mitigations requires careful construction of attack scenarios. Unit tests should simulate the exact callback pattern used in the attack, with a malicious contract that attempts to manipulate oracle reads during remove_liquidity. Fork tests against mainnet Curve pool states provide the most reliable validation.
Mastering the Skill
Read-only reentrancy represents a broader category of vulnerabilities that security researchers call state inconsistency attacks. The same principle applies whenever one protocol reads state from another protocol that might be in a transitional state. To master this skill, practice auditing integrations between protocols, focusing on every point where external contract state is read and used in financial calculations.
The dForce exploit should not have happened. ChainSecurity disclosed the vulnerability to Curve and affected projects in April 2022, and multiple exploits using the same vector preceded dForce. Security is not a one-time activity but a continuous process of monitoring disclosures, updating integrations, and maintaining awareness of the evolving attack landscape across the interconnected DeFi ecosystem.
Disclaimer: This article is for educational purposes only. The techniques described are intended for defensive security research and protocol development. Always practice responsible disclosure and comply with applicable laws.
finally a proper technical walkthrough. most coverage of the dForce exploit just says flash loan attack without explaining the actual reentrancy vector. the virtual price oracle manipulation part is key
the prerequisites section is generous. you need serious EVM knowledge to actually follow this, not just basic solidity