Reentrancy vulnerabilities in smart contracts remain one of the most persistent and damaging attack vectors in blockchain security. Despite years of awareness, new variants like cross-contract and read-only reentrancy are supplanting the classical pattern.
Reentrancy Vulnerabilities in 2026: Patterns from 500 Audits
Reentrancy vulnerabilities in smart contracts remain one of the most persistent and damaging attack vectors in blockchain security. Despite years of awareness, tooling improvements, and best-practice guidelines, reentrancy continues to surface in production code, often with devastating financial consequences. After analyzing patterns across 500 smart contract audits, a clear picture emerges: the vulnerability is evolving, not disappearing. New variants like cross-contract and read-only reentrancy are supplanting the classical single-function pattern, and auditors who rely on outdated detection methods are missing critical findings.
This article distills the most common reentrancy patterns observed across hundreds of audits, examines real-world exploits that shaped the landscape, and provides actionable guidance for developers and security teams working to eliminate this class of vulnerability from their protocols.
Why Reentrancy Still Matters in 2026
The first major reentrancy exploit, the DAO hack of 2016, drained roughly $60 million from Ethereum's earliest decentralized autonomous organization. A decade later, the fundamental mechanism remains the same: an external call transfers control flow to an attacker-controlled contract before the calling function has finished updating its internal state. What has changed is the surface area.
Modern DeFi protocols are deeply compositional. A single transaction may interact with half a dozen contracts across lending pools, DEXs, yield aggregators, and governance modules. Each external call is a potential reentrancy vector. The complexity of cross-contract interactions has grown faster than the tooling to secure them, creating a persistent gap that attackers exploit with increasing sophistication.
The numbers tell the story. Between 2022 and 2025, reentrancy-related exploits accounted for over $400 million in documented losses across major protocols. In 2025 alone, Solv Protocol lost $2.7 million to a cross-function reentrancy vulnerability in its staking mechanism. Cetus Protocol, a prominent DEX on the Sui network, suffered a nine-figure exploit traced back to a reentrancy flaw in its liquidity math. Balancer v2 was drained of roughly $900,000 through a sophisticated read-only reentrancy vector. These are not edge cases; they are emblematic of a systemic problem.
The Four Patterns of Reentrancy
Understanding reentrancy requires understanding its variants. Each pattern presents distinct detection challenges and demands different mitigation strategies.
Single-Function Reentrancy
Single-function reentrancy is the classic form. An external call within a function allows the callee to re-enter the same function before state updates are complete. The DAO hack was a textbook example: the `withdraw` function sent Ether before setting the user's balance to zero, enabling recursive withdrawals.
This pattern is the easiest to detect and the most frequently taught in security courses. Most modern static analysis tools flag it reliably. However, it still appears in audits, typically in newer protocols or in code paths that were added as afterthoughts and never subjected to the same scrutiny as core logic.
The mitigation is well-established: follow the Checks-Effects-Interactions pattern, which dictates that all state mutations should occur before any external call. Alternatively, wrap the function with a reentrancy guard. But as we will see, these mitigations do not extend to the more complex patterns.
Cross-Function Reentrancy
Cross-function reentrancy occurs when an external call in one function allows re-entry into a different function of the same contract that shares state with the first. The attacker does not re-enter the same function; they call a different function that reads stale state and performs actions based on it.
Solv Protocol's $2.7 million loss in 2025 is a clear illustration. The protocol's staking contract allowed users to deposit and withdraw tokens across multiple functions. A vulnerability in one function's external call enabled an attacker to re-enter a different function that read the same shared balance state before it was updated. The result was a drain of funds that appeared, on the surface, to follow all the rules.
Cross-function reentrancy is harder to detect than single-function reentrancy because the vulnerable state is not in the function containing the external call. Auditors must trace state dependencies across the entire contract, not just within individual functions. Static analysis tools that only analyze functions in isolation will miss it.
Cross-Contract Reentrancy
Cross-contract reentrancy extends the problem across contract boundaries. An external call from Contract A to Contract B allows the callee to call back into Contract C, which shares state with Contract A through a third contract or a shared storage pattern. The attacker exploits the fact that Contract C has not yet received updated state information from Contract A.
This pattern is particularly insidious in protocols that use proxy patterns, upgradeable contracts, or complex multi-contract architectures. Balancer v2's 2023 exploit demonstrated this vector: the attacker exploited a reentrancy path that traversed multiple protocol contracts, using a flash loan to amplify the effect. The vulnerability was not in any single contract but in the interaction between them.
Cross-contract reentrancy is the hardest pattern to detect and prevent because no single contract contains the full picture. The vulnerability exists in the gap between contracts, in the assumptions each makes about the state of the others. A reentrancy guard on any individual function is insufficient because the re-entry occurs through a different contract entirely.
Read-Only Reentrancy
Read-only reentrancy is a relatively recent and increasingly important pattern. It occurs when an external call allows re-entry into a view function or a function in another contract that reads stale state and returns incorrect data. The calling function does not directly lose funds; instead, the stale view is used by a third protocol that relies on the view for pricing, balance checks, or other critical calculations.
Balancer v2's 2023 exploit was a read-only reentrancy attack. The attacker exploited a reentrancy vector in Balancer's pool contracts to manipulate the view functions that price tokens. Other DeFi protocols that integrated with Balancer and relied on its on-chain pricing data were affected. The attacker did not steal from Balancer directly but used the manipulated prices to extract value from protocols that trusted Balancer's view functions as accurate.
Read-only reentrancy is particularly dangerous because the vulnerable contract and the exploited contract are different. The protocol with the reentrancy bug may not even be the one that suffers losses. This creates a false sense of security for protocols that believe their own code is safe, while ignoring the risks posed by their dependencies.
Prevention Strategies That Work
Preventing reentrancy vulnerabilities smart contracts requires a layered approach. No single technique is sufficient against all four patterns.
Checks-Effects-Interactions Pattern
The Checks-Effects-Interactions (CEI) pattern is the foundational defense against single-function reentrancy. It dictates that a function should perform all precondition checks first, then update all internal state, and only then make external calls. By the time control flow passes to an external contract, all state is consistent.
CEI is effective against single-function reentrancy and can partially mitigate cross-function reentrancy when applied consistently across all functions in a contract. However, it does not address cross-contract or read-only reentrancy, where the vulnerable state is in a different contract entirely.
The pattern is simple to describe but difficult to enforce consistently. Code reviews should verify that every external call is preceded by complete state updates. Linting rules and custom static analysis checks can automate this verification.
Reentrancy Guards
Reentrancy guards, such as OpenZeppelin's `ReentrancyGuard`, use a mutex-like lock to prevent re-entry into a single contract. They are effective against single-function and cross-function reentrancy within the guarded contract.
Guards have significant limitations. They do not prevent cross-contract reentrancy because the lock is per-contract. They also carry a gas overhead, which can be meaningful in gas-sensitive protocols. Most importantly, they create a false sense of security when developers assume that a guard on one function protects the entire protocol.
Best practice is to use reentrancy guards as a defense-in-depth measure alongside CEI, never as a replacement. Place guards on all functions that make external calls or modify shared state, not just the ones that seem obviously vulnerable.
Pull Payment Patterns
Pull payment patterns replace push-based transfers with a claim-and-withdraw flow. Instead of sending funds to a user within a function, the contract records a credit and the user calls a separate withdrawal function. This eliminates external calls from critical state-transition functions, removing the reentrancy vector entirely.
This pattern is particularly effective for protocols that process user withdrawals. It adds a transaction step for users but eliminates the most common reentrancy vector in payment processing. Combined with CEI and reentrancy guards, it provides robust protection for fund-handling functions.
State Machine Patterns
State machine patterns enforce strict state transitions, preventing re-entry by making invalid state transitions impossible. Each operation moves the contract from one state to another, and re-entry attempts fail because the contract is no longer in the expected state.
For example, a lending protocol might define states like `Deposit`, `Borrow`, `Repay`, and `Liquidate`. Each function checks that the contract is in the correct state before executing and transitions to the next state atomically. A re-entrancy attempt fails because the state has already moved forward.
This pattern is powerful because it prevents reentrancy at the architectural level rather than relying on guards or ordering conventions. However, it requires upfront design effort and can make contract logic more complex.
How Modern Auditors Detect Reentrancy
Detection methodology has evolved significantly. The tools and techniques available in 2026 bear little resemblance to the manual code reviews of the early Ethereum era.
Static Analysis
Static analysis tools remain the first line of defense. Modern tools such as Slither, Mythril, and custom linting frameworks can detect single-function and cross-function reentrancy with high reliability. They trace control flow and identify external calls that precede state mutations, flagging potential violations of the CEI pattern.
However, static analysis has well-known limitations. It struggles with cross-contract and read-only reentrancy, where the vulnerability spans multiple contracts. It also produces false positives that require manual triage, consuming auditor time and potentially masking genuine issues.
The best audit workflows use static analysis as a starting point, not an endpoint. Tools identify candidates; auditors validate and investigate further.
Formal Verification
Formal verification uses mathematical proofs to verify that a contract satisfies a specification. For reentrancy, this means proving that no sequence of external calls can lead to inconsistent state.
Formal verification is the gold standard for critical protocol components. It can detect all four reentrancy patterns, including cross-contract and read-only variants, because it reasons about the entire system rather than individual functions. Tools like Certora and Halmos have made formal verification more accessible to smart contract developers.
The trade-off is cost. Formal verification requires significant expertise to specify properties correctly and can be prohibitively expensive for smaller protocols. It is best applied to core protocol logic, such as token contracts, lending pools, and governance modules, rather than to every function in a codebase.
Fuzzing
Fuzzing generates random or semi-random inputs to discover unexpected behavior. For reentrancy, fuzzing tools like Echidna and Medusa can simulate re-entry scenarios by generating call sequences that exercise external call paths.
Fuzzing is particularly effective at finding edge cases that static analysis misses. It can discover reentrancy vectors that require specific state configurations or unusual call sequences. However, fuzzing is probabilistic; it can prove the existence of bugs but cannot prove their absence. A fuzzing campaign that finds no reentrancy vulnerabilities does not guarantee that none exist.
The most effective audit teams combine all three approaches. Static analysis provides broad coverage, formal verification provides deep assurance for critical paths, and fuzzing explores the boundary conditions that both might miss.
Lessons from 500 Audits
Across the audits analyzed, several patterns recur with enough frequency to be considered systemic.
First, reentrancy vulnerabilities are disproportionately found in newer code. Protocols that have been audited multiple times tend to have fewer findings, not because reentrancy is harder to introduce, but because the existing code has been hardened through repeated review. New features, integrations, and protocol extensions are the most common entry points for reentrancy bugs.
Second, cross-contract and read-only reentrancy are under-detected relative to their prevalence. Auditors and tools that focus on single-contract analysis consistently miss these patterns. Protocols that rely solely on single-contract audits are likely harboring undetected vulnerabilities.
Third, the assumption that reentrancy guards are sufficient is widespread and dangerous. Many protocols use `nonReentrant` modifiers on obvious entry points but leave view functions and internal helpers unguarded, creating vectors for read-only and cross-contract attacks.
Fourth, composability is the primary driver of new reentrancy risk. Every protocol integration, every oracle dependency, every callback hook adds to the attack surface. Protocols that minimize external dependencies and carefully validate the behavior of those they include are consistently more secure.
Fifth, testing for reentrancy is often inadequate. Unit tests that only exercise happy paths, or that mock external calls rather than simulating actual re-entry, provide a false sense of security. Effective testing requires adversarial scenarios that specifically attempt re-entry during state transitions.
Actionable Takeaways
Based on the patterns observed across these audits, here are concrete steps that protocol teams can take today:
- Apply CEI consistently across every function, not just the ones that handle funds. Any function that makes an external call should complete all state mutations before that call.
- Use reentrancy guards as defense-in-depth, not as a primary control. Apply them to all state-changing functions, and recognize that they do not protect against cross-contract or read-only reentrancy.
- Audit for cross-contract reentrancy explicitly. Single-contract audits are insufficient. Ensure your audit scope covers all contracts that share state or interact with your protocol.
- Treat view functions as part of your attack surface. Read-only reentrancy exploits view functions. Validate that any view function that could be called during a re-entry returns safe, conservative values.
- Integrate formal verification for core logic. The cost of formal verification is justified for the contracts that hold the most value. Apply it to your token contracts, lending pools, and governance modules.
- Design for pull payments where possible. Shifting from push to pull payment patterns eliminates the most common reentrancy vector entirely.
- Re-audit after every integration. New composability is the most common source of new reentrancy vulnerabilities. Treat every integration as a new audit scope.
- Include adversarial reentrancy tests in your test suite. Simulate actual re-entry scenarios, not just happy-path execution. Use tools like Echidna or Medusa to discover edge cases.
- Maintain a dependency map of all external contracts your protocol interacts with. Understand which state each dependency reads and writes, and verify that state is consistent at every call boundary.
- Educate your team continuously. Reentrancy is a well-known vulnerability, but its variants are not. Ensure every developer understands cross-function, cross-contract, and read-only reentrancy, not just the classic single-function pattern.
Conclusion
Reentrancy vulnerabilities in smart contracts have not been solved; they have evolved. The patterns observed across hundreds of audits show that while single-function reentrancy is well-understood and increasingly well-mitigated, cross-function, cross-contract, and read-only reentrancy remain persistent threats. The Solv Protocol, Cetus Protocol, and Balancer v2 exploits are not anomalies; they are data points in a clear trend toward more complex, multi-contract reentrancy attacks.
The good news is that detection and prevention techniques have evolved as well. Formal verification, advanced fuzzing, and improved static analysis give auditors powerful tools for identifying reentrancy before it reaches production. The combination of architectural patterns like state machines, pull payments, and defense-in-depth guards provides a robust mitigation framework.
The challenge is adoption. Too many protocols rely on outdated assumptions about reentrancy, applying guards and CEI to obvious functions while leaving cross-contract vectors unaddressed. The protocols that fare best are those that treat reentrancy as a systemic risk, not a per-function checklist item. Understanding reentrancy vulnerabilities smart contracts face today requires looking beyond individual functions and toward the interactions that define modern DeFi.
