We use cookies to keep the site working, understand how it’s used, and measure our marketing. You can accept everything, reject non-essentials, or pick what’s on.
Outcome: Exploit closed before mainnet — Zero user funds at risk
Blockchain Security Case Study June 2025
Table of Contents
Note: This Table of Contents is generated via field codes. To ensure page number accuracy after editing, please right-click the TOC and select "Update Field."
Two Weeks from Catastrophe
A DeFi yield protocol was fourteen days from its mainnet launch. Twenty-four million dollars in committed total value locked (TVL) had already been secured from institutional LPs and early depositors. The codebase had passed an internal review, unit test coverage exceeded 90%, and every public function included the standard OpenZeppelin ReentrancyGuard modifier. The team was confident. They scheduled the deployment ceremony and began drafting the launch announcement.
Then the audit report landed. Buried in the findings — initially flagged by Slither as a low-severity CEI pattern violation — was a cross-function reentrancy path that could drain the entire protocol treasury in a single transaction. The attack did not exploit a missing modifier. It exploited the architecture of the contract system itself: a state variable updated in one function could be re-read and exploited during a reentrant callback through a second function that shared that same state. The guard on each individual function was correct. The guard on the interaction between them did not exist.
This article presents a detailed technical account of how that vulnerability was identified, proven, and resolved. It examines reentrancy attack fundamentals, the critical difference between single-function and cross-function reentrancy, why static analysis tools alone are insufficient, and how manual call-graph tracing combined with Foundry proof-of-concept development uncovered a class of vulnerability that automated tools rated as low severity. The case study serves as a cautionary tale for DeFi development teams and illustrates why comprehensive smart contract auditing remains the last line of defence against catastrophic financial loss.
The DeFi Security Landscape
A Brief History of Reentrancy Attacks
The reentrancy attack is one of the oldest and most destructive classes of smart contract vulnerabilities. Its most famous manifestation was the 2016 attack on The DAO, a decentralised investment fund built on Ethereum. On 17 June 2016, an attacker exploited a reentrancy vulnerability in The DAO's withdrawal function to siphon approximately 3.6 million ETH — then valued at roughly $60 million, representing roughly one-third of all ETH in circulation at the time. The attack exploited a classic pattern: the contract sent Ether to the caller before updating the caller's internal balance, allowing the attacker's fallback function to recursively call the withdrawal function and extract funds multiple times before the balance was finally decremented.
The DAO hack did not merely destroy a project. It fractured the Ethereum community, triggered a contentious hard fork that created Ethereum Classic as a separate chain, and fundamentally altered the trajectory of blockchain governance. More importantly for the security community, it established reentrancy as the canonical example of smart contract vulnerability and catalysed an entire industry of formal verification, static analysis tools, and security auditing practices.
Since The DAO, reentrancy attacks have evolved significantly. While the original single-function reentrancy is now well understood and easily mitigated, attackers have developed increasingly sophisticated variants. Cross-function reentrancy, where an attacker exploits state inconsistencies between two or more functions in the same contract, has become one of the most dangerous attack vectors in modern DeFi. Cross-contract reentrancy, where the attacker leverages interactions between multiple contracts in a protocol suite, adds another layer of complexity. Read-only reentrancy, where the attacker manipulates view functions that external protocols rely on for price discovery, has been used in attacks against major lending platforms.
The Scale of DeFi Exploits
The financial toll of smart contract vulnerabilities in DeFi has been staggering. According to data from DeFi Llama and various security research firms, over $7 billion has been lost to exploits, hacks, and rug pulls across DeFi protocols since 2020. Reentrancy specifically accounts for a significant portion of these losses, consistently ranking among the top three vulnerability categories in both frequency and financial impact. The OWASP Smart Contract Top 10, published as part of the broader OWASP initiative for blockchain security, lists reentrancy attacks as SC-01 — the number-one risk category.
Notable reentrancy-related exploits beyond The DAO include the 2021 Cream Finance exploit ($130 million), the 2022 Fei Protocol vulnerability ($80 million at risk), and numerous smaller incidents across lending protocols, decentralised exchanges, and yield aggregators. The common thread in many of these cases is not that the developers were negligent — many had passed internal reviews and even third-party audits — but that the specific reentrancy variant exploited was not covered by the audit scope, was missed by automated tools, or emerged from a complex interaction between multiple protocol components that no single analysis technique could fully capture.
Why Audits Matter More Than Ever
As DeFi protocols handle increasingly large sums of institutional capital, the cost of a single undetected vulnerability grows proportionally. A protocol launching with $24 million in TVL — as in the case studied here — represents not just user deposits but institutional trust, regulatory relationships, and market positioning. A successful exploit at that scale would not only destroy user funds but likely end the protocol's existence, expose the development team to legal liability, and damage the broader DeFi ecosystem's credibility with institutional investors.
Smart contract audits serve multiple functions beyond vulnerability detection. They provide a formal trail of due diligence for institutional investors and regulators. They establish a security baseline that can be referenced in insurance applications. They create a knowledge transfer opportunity where auditors share best practices and emerging threat intelligence with development teams. In the case documented here, the audit report itself became a critical asset for institutional due diligence, demonstrating that the protocol had been rigorously examined before any user funds were placed at risk.
Reentrancy Attack Fundamentals
The Mechanics of Single-Function Reentrancy
At its core, a reentrancy attack exploits the order of operations in a smart contract function. When a contract sends Ether or tokens to an external address via an external call, control flow transfers to the recipient's fallback or receive function. If that recipient is a malicious contract, it can execute arbitrary code during the callback — including calling back into the original function before the original invocation has completed its execution.
The classic vulnerable pattern follows this sequence:
Step 1: The user calls a withdraw function on the target contract.
Step 2: The contract checks the user's balance (sufficient funds exist).
Step 3: The contract sends Ether or tokens to the user via an external call (e.g., .call{value: amount}("")).
Step 4: The attacker's receive function triggers, calling the withdraw function again.
Step 5: The contract checks the user's balance — which has not yet been decremented — and allows another withdrawal.
Step 6: Steps 3–5 repeat until the contract is drained, at which point the original function's balance update finally executes.
The fundamental issue is that the external call in Step 3 acts as a reentrancy point, and the state update (balance decrement) occurs after the call rather than before. This is a violation of the Checks-Effects-Interactions (CEI) pattern, which mandates that all state changes (effects) must be completed before any external calls (interactions).
Cross-Function Reentrancy: The Hidden Threat
Cross-function reentrancy is a more sophisticated variant that is significantly harder to detect and defend against. Unlike single-function reentrancy, where the vulnerable function re-enters itself, cross-function reentrancy exploits the interaction between two or more functions that share state variables but apply the ReentrancyGuard modifier independently.
Consider the following scenario, which mirrors the vulnerability found in the audited protocol:
Function A (depositYield) accepts a deposit, updates the user's deposited balance, and then calls an external hook on a yield strategy contract. Function B (withdraw) reads the user's deposited balance and transfers tokens to the user. Both functions have the nonReentrant modifier. However, during the external call in Function A, the yield strategy contract — which may be controlled by an attacker — can call Function B. Because Function B's nonReentrant guard checks a different lock state than Function A's guard (in the standard OpenZeppelin implementation, the lock is per-function, not per-contract, depending on the Solidity version and modifier used), the reentrant call to Function B succeeds. The attacker can then withdraw funds that were credited in Function A but have not yet been fully committed to the protocol's accounting.
The critical insight is that each function individually follows correct security practices: Function A updates state before its external call, and Function B has its own reentrancy guard. The vulnerability exists only in the interaction between them — a state inconsistency that emerges when control flow jumps from one function to another during execution. This is why automated tools that analyse functions in isolation frequently miss cross-function reentrancy: the vulnerability is a property of the call graph, not of any single function.
The CEI Pattern: Checks, Effects, Interactions
The Checks-Effects-Interactions (CEI) pattern is the most widely cited defensive coding practice in Solidity development. Codified by the Ethereum community in the early years of smart contract development, CEI prescribes a strict ordering of operations within every function:
Phase
Description
Example
Checks
Validate all preconditions and require statements
Effects
Update all state variables
Interactions
Make all external calls
When followed correctly, CEI prevents single-function reentrancy because all state changes are committed before any external call can trigger a re-entrant callback. However, CEI has an inherent limitation: it is defined at the function level. It does not account for state that is shared across functions or across contracts within a protocol. In the cross-function reentrancy scenario, each function may individually follow CEI, but the shared state between functions creates a window of vulnerability that CEI does not address.
In the case study protocol, Slither flagged a CEI violation as low severity because the specific function it analysed did update its local state before the external call. The tool correctly identified that the individual function deviated from best practice but could not determine that the deviation enabled a cross-function exploit path. This is a known limitation of pattern-based static analysis: it can identify syntactic violations but struggles to assess their semantic impact across function boundaries.
Detection and Analysis Methodology
Slither Static Analysis and Its Limitations
Slither, developed by Trail of Bits, is the most widely used static analysis framework for Solidity smart contracts. Written in Python, Slither parses Solidity source code into an intermediate representation, builds a detailed control-flow graph (CFG) and call graph, and runs a suite of over 80 built-in detectors that identify common vulnerability patterns, code quality issues, and gas optimisation opportunities. Slither is typically the first tool run in any professional audit because it is fast (completing in seconds), requires no test setup, and provides immediate, actionable findings.
In this audit, Slither was configured with its full detector suite and run against the entire protocol codebase. Among its outputs was a finding classified as low severity: a CEI pattern violation in the depositYield function. The detector correctly noted that an external call to the yield strategy contract occurred before all state effects were fully committed. However, Slither's vulnerability detectors operate primarily at the function level. While they can trace data flow within a function and across simple call chains, they do not perform full cross-function reentrancy analysis by default. The CEI violation was reported as a coding hygiene issue, not as a critical exploit vector.
This is not a deficiency unique to Slither. All static analysis tools face a fundamental trade-off between precision and recall. Flagging every possible cross-function reentrancy path would generate thousands of false positives, rendering the tool effectively useless for human auditors. The practical approach — which Slither, Mythril, and similar tools adopt — is to report pattern violations and potential issues at low severity, relying on human analysts to assess their true impact through manual analysis.
Manual Call-Graph Tracing
The critical breakthrough in this audit came from manual call-graph tracing. After reviewing Slither's low-severity CEI finding, the lead auditor decided to trace the full call graph starting from the depositYield function's external call. This involved manually mapping every function that could be invoked as a result of the external call to the yield strategy contract, and then checking whether any of those functions could callback into the Vault contract in a way that exploited the partially-updated state.
The call-graph tracing process followed this systematic approach:
Step 1: Identify all external calls in the target function and the contracts they invoke.
Step 2: For each invoked contract, determine whether it is upgradeable, attacker-controlled, or otherwise capable of arbitrary code execution during the callback.
Step 3: Map all public and external functions in the target contract that read or modify the same state variables as the original function.
Step 4: Check whether any reentrancy guard on those functions would prevent a reentrant call from the external contract.
Step 5: Construct the exploit path by simulating the attacker's sequence of calls and verifying that each step succeeds.
In Step 3, the auditor discovered that the withdraw function read the same deposited balance that depositYield had just updated. In Step 4, the auditor confirmed that because the two functions used separate reentrancy guard instances (a consequence of the original guard implementation before EIP-1153 transient storage was available), the reentrant call to withdraw would pass the guard check. This confirmed the cross-function reentrancy path.
Foundry Proof-of-Concept Exploit
With the theoretical vulnerability identified, the next step was to construct a concrete proof-of-concept (PoC) exploit. The audit team used Foundry, a Solidity-native testing framework developed by Paradigm, to build and execute the exploit against a local fork of the protocol's deployment configuration. Foundry was chosen for several reasons: its native Solidity test syntax allows exploits to be written in the same language as the target contracts, its forge fuzzing engine can quickly explore edge cases, and its ability to fork mainnet state enables testing against realistic contract configurations.
The PoC exploit was structured as follows:
1. Fork setup: Foundry's vm.createSelectFork was used to create a local fork at a specific mainnet block number, providing realistic token balances and contract states.
2. Attacker contract: A malicious YieldStrategy contract was deployed that implemented a custom deposit function. When the Vault called this function, the attacker contract's receive hook triggered a callback to Vault.withdraw().
3. Exploit execution: The attacker deposited a small amount of yield tokens, triggering the cross-function reentrancy loop. The PoC demonstrated that in a single transaction, the attacker could withdraw significantly more than their deposited amount, ultimately draining the Vault's entire token balance.
4. Verification: After the exploit transaction, the PoC asserted that the Vault's token balance was zero and the attacker's balance exceeded the Vault's pre-exploit holdings.
The PoC ran in under five seconds on the local fork, producing a clear and unambiguous demonstration of the vulnerability. The audit team included the full Foundry test code in their report, enabling the development team to reproduce the exploit independently and verify the fix.
Remediation and Verification
The Fix: CEI Compliance and Transient Guards
The remediation addressed the vulnerability on two levels. The primary fix was architectural: the depositYield function was refactored to move all balance updates before the external call to the yield strategy, restoring strict CEI compliance. Specifically, the function now decremented the user's pending-yield balance, updated the total protocol TVL, and committed all accounting changes to storage before invoking the external strategy contract. This eliminated the window of inconsistency that the cross-function reentrancy had exploited.
The secondary fix added a defence-in-depth layer: the protocol adopted OpenZeppelin's ReentrancyGuardTransient, which leverages EIP-1153 transient storage (introduced in the Ethereum Cancun upgrade) to implement a single contract-wide reentrancy lock. Unlike the classic ReentrancyGuard, which uses a persistent storage slot that persists between transactions, the transient variant uses transient storage that is automatically cleared at the end of each transaction. This provides two critical advantages:
Gas efficiency: Transient storage operations cost significantly less gas than persistent storage operations (transient storage costs ~2100 gas for a warm store versus ~20,000 gas for a cold SSTORE). For protocols where the reentrancy guard is called on every transaction, this reduces per-transaction gas costs measurably.
Contract-wide protection: Because transient storage is scoped to the contract address (not the function), a ReentrancyGuardTransient modifier applied to any function in the contract prevents reentrancy into any other function with the same modifier. This eliminates the cross-function reentrancy vector entirely, even if individual functions have CEI violations.
The combination of CEI compliance and a contract-wide transient guard provides robust defence against both single-function and cross-function reentrancy. The CEI fix is the primary defence — it eliminates the vulnerability at the source — while the transient guard provides a safety net that would catch any future CEI regressions introduced during maintenance or feature development.
Secondary Finding: Unchecked ERC-20 Return Value
During the audit, the team also identified a secondary finding: an unchecked ERC-20 return value in the token transfer logic. Some ERC-20 token implementations (notably USDT and other older tokens) return false instead of reverting when a transfer fails. Solidity's low-level call returns a boolean, and if this boolean is not explicitly checked, a failed transfer may silently succeed from the contract's perspective, leading to accounting inconsistencies.
The fix was straightforward: the protocol adopted OpenZeppelin's SafeERC20 library, which wraps all ERC-20 operations with explicit return-value checks and provides safeTransfer and safeTransferFrom functions that revert on failure. While this finding was classified as medium severity (it requires a specific token implementation to exploit), fixing it alongside the reentrancy issue eliminated a second potential attack vector and demonstrated the thoroughness of the audit process.
Re-Testing and Verification
After the development team implemented both fixes, the audit team performed a complete re-test. This involved:
Re-running Slither: The full static analysis suite was re-executed against the updated codebase. The CEI violation finding was resolved, and no new findings were introduced by the fix.
Re-executing the PoC: The original Foundry exploit test was run against the patched code. The reentrancy attack now reverted at the transient guard, confirming that the exploit path was fully closed.
Regression testing: All existing protocol unit tests passed against the patched code, confirming that the fixes did not introduce functional regressions.
Additional edge-case testing: The audit team wrote additional Foundry fuzz tests targeting the withdraw and depositYield functions to verify that no other reentrancy paths existed.
The re-test cycle took approximately two days, bringing the total audit timeline to roughly one week from initial engagement to final sign-off. Given that the protocol was fourteen days from launch when the audit began, the timing was tight but manageable.
Institutional Audit Requirements
What Institutions Expect from an Audit
The protocol's institutional investors required a formal audit report before allowing the mainnet launch to proceed. This is now standard practice in DeFi: any protocol handling institutional capital is expected to present a third-party audit from a reputable firm, and many institutional LPs have internal checklists that the audit must satisfy. The key requirements typically include:
Requirement
Description
Why It Matters
Scope completeness
All contracts in the deployment scope must be covered, including proxy contracts, strategy implementations, and price oracle integrations
Severity classification
Findings must be classified by severity (Critical/High/Medium/Low/Informational) with clear criteria
Proof-of-concept exploits
Critical and high-severity findings must include executable PoC code that demonstrates the vulnerability
Fix verification
The auditor must re-test after fixes are applied and confirm remediation
Executive summary
A non-technical summary suitable for board-level stakeholders
Regulatory alignment
The report should reference relevant security standards (OWASP SC Top 10, SWC Registry)
In this case, the audit report satisfied all of these requirements. The PoC exploit provided unambiguous evidence of the vulnerability's severity, and the re-test section documented that both the reentrancy and the unchecked return-value issues were fully resolved. The executive summary was prepared in consultation with the protocol's legal counsel to ensure it met the due-diligence requirements of the specific institutional investors involved.
The Audit as a Competitive Advantage
Beyond satisfying regulatory and investor requirements, a rigorous audit can serve as a competitive differentiator in the crowded DeFi market. Protocols that publish their audit reports publicly — including findings and remediation details — signal transparency and security maturity. In a market where user trust is paramount and institutional capital is increasingly selective, a comprehensive audit is not merely a cost centre but a strategic asset that can attract deposits, reduce insurance premiums, and accelerate partnerships with other DeFi protocols.
Counterarguments and Limitations
While this case study demonstrates the value of manual audit methodology, several counterarguments and limitations should be acknowledged:
Tooling is improving rapidly: Modern static analysis tools (Slither 0.10+, Mythril with cross-contract analysis, and emerging formal verification tools like Certora) are increasingly capable of detecting cross-function reentrancy patterns. As tooling improves, the gap between automated detection and manual analysis narrows. However, as of this writing, no automated tool can fully replace the contextual reasoning that an experienced auditor applies when tracing complex call graphs.
Audit cost and access: Comprehensive manual audits are expensive, typically ranging from $15,000 to $100,000+ depending on scope. For small or bootstrapped protocols, this cost can be prohibitive. Open-source audit tooling, community review programmes (such as Code4rena and Sherlock), and bug bounty platforms provide alternative paths, but they may not offer the depth of a dedicated audit engagement.
The stopping problem: An audit can only verify the code as written at the time of review. It cannot prevent vulnerabilities introduced by subsequent upgrades, nor can it guarantee that the deployment configuration matches the audited code. Continuous monitoring (e.g., OpenZeppelin Defender, Forta) and upgrade governance processes are essential complements to a one-time audit.
False sense of security: An audit report, even a clean one, does not guarantee that a protocol is vulnerability-free. It guarantees that a specific set of reviewers, using a specific set of techniques, did not find specific classes of issues within a specific scope. Over-reliance on an audit as a security guarantee is itself a risk. Defence in depth — combining audits with formal verification, runtime monitoring, bug bounties, and conservative upgrade governance — is the only robust approach.
ReentrancyGuardTransient availability: The transient storage fix described in this article requires the EIP-1153 upgrade (Cancun hard fork), which was activated on Ethereum mainnet in March 2024. For protocols deployed on chains that have not yet implemented EIP-1153, or that need to support pre-Cancun compatibility, alternative approaches (such as a single persistent reentrancy lock across all functions) are necessary, albeit with higher gas costs.
Conclusion and Implications
This case study illustrates a fundamental truth about smart contract security: automated tools are necessary but not sufficient. Slither correctly identified a CEI pattern violation, but classified it as low severity because its function-level analysis could not assess the cross-function exploit path. Only manual call-graph tracing — the systematic, human-driven analysis of how functions interact through shared state and external calls — revealed the full severity of the vulnerability. And only a concrete Foundry proof-of-concept exploit could demonstrate with absolute certainty that the vulnerability was exploitable in a single transaction.
The outcome was definitive: the exploit was closed before mainnet launch, zero user funds were placed at risk, and the audit report provided the institutional due-diligence documentation that the protocol's investors required. The secondary finding (unchecked ERC-20 return value) was also resolved, further strengthening the protocol's security posture. In total, the one-week audit engagement prevented what could have been a $24 million loss and a reputational catastrophe.
For DeFi development teams, the lessons are clear:
Never rely solely on automated tools for security assurance. Slither, Mythril, and similar tools are valuable first-pass screening mechanisms, but they cannot replace human analysis of cross-function and cross-contract interactions.
Design for auditability from the start. Clear function boundaries, well-documented state machines, and minimal external call surfaces make both automated and manual analysis more effective.
Require concrete proof-of-concept exploits for all critical and high-severity findings. A theoretical vulnerability description is orders of magnitude less actionable than executable exploit code.
Adopt defence-in-depth strategies. CEI compliance, contract-wide reentrancy guards, SafeERC20 for token transfers, and comprehensive testing are not alternatives to auditing — they are complementary layers that reduce the probability and impact of any single vulnerability.
Treat the audit report as a living document. Security is not a one-time event but a continuous process. Post-launch monitoring, bug bounty programmes, and upgrade governance ensure that the security assurances established at launch are maintained throughout the protocol's operational lifetime.
The DeFi ecosystem continues to mature, and with that maturation comes both larger capital flows and more sophisticated attack vectors. Cross-function reentrancy, once considered an edge case, is now recognised as one of the most dangerous vulnerability classes in modern protocol design. As this case study demonstrates, the difference between a secure launch and a catastrophic exploit can come down to whether an auditor traces one more function call in the call graph. In a $24 million protocol, that trace is worth every penny.
[3] J. Feist et al., "Slither: A Static Analysis Framework for Smart Contracts," IEEE/ACM International Workshop on Emerging Trends in Software Engineering for Blockchain (WETSEB), 2019.