On December 17, 2023, the NFT trading platform NFT Trader lost approximately $3 million in high-value NFTs to a reentrancy attack — the same class of vulnerability that famously drained The DAO of $60 million in 2016. Despite being one of the oldest and most well-documented smart contract vulnerabilities, reentrancy attacks continue to plague the crypto ecosystem, costing hundreds of millions of dollars cumulatively. This article explains what reentrancy attacks are, how they work, and how developers can prevent them, using recent real-world examples to illustrate each concept.
The Core Concept
A reentrancy attack occurs when an external contract calls back into the calling contract before the calling contract has finished updating its internal state. In simpler terms, imagine a bank withdrawal function that sends you money before deducting your balance. If you can call the withdrawal function again before the balance is updated, you can withdraw the same funds repeatedly until the contract is drained. The attack exploits the execution order of smart contracts, where external calls (sending ether or calling other contracts) can trigger callback functions that re-enter the original function.
The critical insight is that smart contract execution is synchronous within a single transaction but can involve multiple nested calls. When contract A calls contract B, contract B can call back into contract A. If contract A has not yet updated its state when contract B calls back, contract B sees the old state and can exploit the inconsistency.
Step-by-Step Breakdown
Consider the classic vulnerable contract pattern. A simple ether storage contract might have a withdraw function that first sends ether to the caller and then updates the caller’s balance. The vulnerability exists in this ordering: the external call to send ether happens before the state update that reduces the balance.
When the attacker’s contract receives the ether, its fallback function is triggered. This fallback function can call the withdraw function again. Because the balance has not yet been reduced, the contract still sees the original balance and sends more ether. This cycle continues, with each iteration sending ether before the balance is updated, until the contract is drained or the gas limit is reached.
In the NFT Trader attack, the same principle applied to NFT transfers rather than ether withdrawals. The attacker exploited a function that transferred NFTs based on approval state. By re-entering the transfer function through a callback before the approval state was cleared, the attacker could transfer the same NFTs multiple times, draining high-value assets including Bored Apes and CryptoPunks from users who had previously granted approvals to the platform.
Visualizing the Attack
Think of the attack flow as a loop. The attacker calls the vulnerable function. The function performs an external call to the attacker’s contract. The attacker’s contract calls the vulnerable function again. The function has not yet reached the state update, so it processes the second call as if the first had not happened. This creates a recursive loop where each iteration extracts value before the contract can update its records. The loop terminates when either the contract runs out of funds or the transaction runs out of gas.
The key technical detail is that the Ethereum Virtual Machine processes calls in a depth-first manner. When contract A calls contract B, contract B executes completely (including any calls back to A) before control returns to A and it continues executing. This means the re-entrant call executes while A is still in the middle of its function execution, before reaching the state update lines.
Mitigation Strategies
The most fundamental defense is the checks-effects-interactions pattern. This pattern requires that all condition checks happen first, all state modifications happen second, and all external interactions happen last. By reordering the vulnerable withdraw function to update the balance before sending the ether, the reentrancy opportunity is eliminated because the balance is already zero when the attacker tries to withdraw again.
A more explicit defense is the reentrancy guard modifier, popularized by OpenZeppelin’s ReentrancyGuard contract. This modifier uses a status variable that is set to indicate a function is currently executing. If the function is called again before the first call completes, the modifier rejects the call. This provides a simple, reliable lock mechanism that prevents any reentrant calls regardless of the specific function logic.
For more complex contracts, the pull-over-push pattern can eliminate the need for external calls entirely. Instead of sending funds proactively, contracts record that a user is entitled to withdraw and let the user initiate the withdrawal themselves. This shifts the external call to a separate transaction, making reentrancy impossible within a single call.
Lessons From Recent Exploits
The persistence of reentrancy vulnerabilities in 2023 highlights several systemic issues in smart contract development. First, the rush to ship features often leads teams to skip thorough security reviews of their contract logic. Second, even experienced developers can introduce reentrancy through complex state interactions that are not immediately obvious during code review. Third, the increasing complexity of DeFi protocols, with their composability and cross-contract interactions, creates new reentrancy surfaces that were not present in simpler contracts.
The NFT Trader attack specifically exploited a reentrancy in an NFT transfer function, a pattern that is less commonly discussed than the classic ether withdrawal reentrancy. This illustrates an important lesson: reentrancy can occur in any context where an external call is made before state is updated, not just in withdrawal functions. Developers must audit all external calls, regardless of their purpose, for potential reentrancy vulnerabilities.
Key Takeaways
Reentrancy remains one of the most expensive vulnerability classes in smart contract security. The defense is well-understood and straightforward to implement: follow the checks-effects-interactions pattern, use reentrancy guards, and consider pull-over-push patterns for fund transfers. Every external call should be treated as a potential reentrancy point and audited accordingly. The tools to prevent these attacks exist — the industry needs the discipline to use them consistently. Until then, reentrancy will continue to cost millions.
Disclaimer: This article is for educational purposes only and does not constitute financial or investment advice. Always conduct your own research before making any investment decisions.
NFT Trader losing $3M to reentrancy in 2023 is wild. the DAO hack was 7 years earlier and the fix is literally one modifier
ReentrancyGuard costs like 20k gas. projects skipping it to save pennies then losing millions should be criminally negligent
map_slot_ 20k gas is the funniest part. teams spend weeks optimizing a loop to save 5k gas but wont add a reentrancy guard that costs 20k and prevents millions in losses
Soren L the 20k gas vs 3M loss comparison is brutal. teams optimize loop gas to save pennies then skip a modifier that costs less than a storage write
the bank analogy in this article is the clearest explanation of reentrancy ive seen. you send money before updating balance, of course it breaks
the DAO hack was 2016 and people are still shipping the same bug in 2023. checks-effects-interactions is literally day 1 of any solidity tutorial
the DAO hack used the exact same pattern in 2016 and we’re still seeing it in 2023. OpenZeppelin’s ReentrancyGuard is literally a 5 line import, no excuse
devansh is right that the DAO pattern from 2016 is still repeating. seven years of openzeppelin libraries and projects still manage to ship vulnerable contracts. the tooling exists, the willingness to use it doesnt
Kwanda N. the DAO to NFT Trader pipeline is 7 years of the same bug. the willingness to use tooling that already exists is the actual vulnerability, not the code itself
checks-effects-interactions is literally taught in solidity lesson 1. how do professional auditors still miss this
audit_grind_ the problem isnt that auditors miss it, its that projects skip audits entirely to save money. NFT Trader probably spent more on marketing than security
pk3r_ NFT Trader spending more on marketing than security is the business model of like half the projects in this space. the incentive structure rewards hype over correctness
The bank withdrawal analogy makes this so clear. Why is this still happening seven years later?
the reentrancy guard modifier costs like 20k gas. thats literally pennies to prevent a $3M exploit. the ROI on basic security is absurd
the bank analogy is perfect. imagine withdrawing cash and the teller forgets to deduct from your balance. same exact bug
niels has the whole thing figured out. shipping fast beats security until the $3M headline hits. nft trader definitely spent more on partnerships than on a single security review
NFT Trader getting hit in 2023 with a 2016 bug is peak crypto. seven years of free OpenZeppelin tooling and teams still ship without ReentrancyGuard
NFT Trader losing 3M to the same bug class that drained The DAO in 2016. seven years later and people still dont implement checks-effects-interactions properly. embarrassing
kol_n the bank withdrawal analogy in the article is perfect. if you send money before updating the balance you deserve to get drained. this is day one Solidity stuff
ReentrancyGuard from OpenZeppelin prevents this with one modifier. there is no excuse for any contract in 2023 to be vulnerable. use the tools that exist
reentrantz ReentrancyGuard is one import and one modifier. the gas cost is negligible. projects skipping this in 2023 should be liable for user losses imo
reentrant_ one modifier and 20k gas to prevent a 3M drain. the fact that NFT Trader shipped without ReentrancyGuard in 2023 should have legal consequences
checks-effects-interactions is literally day 1. How do projects still skip this?
Robert C. the answer is always the same. shipping fast, skipping audits, hoping nobody notices. security is treated as a cost center until its a $3M loss
checks-effects-interactions has been taught since 2016 and professional teams still ship vulnerable contracts in 2023. the gap between audited code and shipped code is the real vulnerability
Alexey V. the gap between audited code and shipped code is where reentrancy lives. teams get an audit on version X, then deploy version Y with a new feature nobody reviewed. happens every cycle