The Anatomy of a DeFi Hack: How Reentrancy Vulnerabilities Drain Millions of Smart Contracts
The reentry exploit is one of the most devastating bugs in Solidity, allowing malicious contracts to repeatedly withdraw funds before updating balances.

The Anatomy of a DeFi Hack: How Reentrancy Vulnerabilities Drain Millions of Smart Contracts
In the decentralized financial (DeFi) ecosystem, smart contract code is law. If a smart contract has a small logical flaw, attackers can irreversibly drain millions of dollars in minutes. The most historical and destructive example of this is the reentry or Reentrancy vulnerability.
This vulnerability caused the collapse of the investment fund The DAO in 2016, forcing a hard fork in the Ethereum network that gave rise to Ethereum (ETH) and Ethereum Classic (ETC).
The execution flow in the EVM
In the Ethereum Virtual Machine (EVM), when a smart contract sends funds (Ether) to another address that happens to be a smart contract, the receiving contract can automatically execute code through special functions called fallback or receive.
The re-entry issue occurs when the issuing contract transfers the funds before updating the user's account balance in its internal database.
The attack typically proceeds as follows:
- Withdrawal call: A malicious contract requests to withdraw your balance deposited in a loan or investment smart contract.
- Transfer of funds: The victim contract reads the attacker's balance, sees that he has funds and sends the money to him through an external call (
call). - Malicious interruption: Upon receiving the funds, the attacker's contract activates its
fallbackfunction. Instead of terminating the transaction, this malicious code calls the victim contract's withdrawal function again. - Drain loop: Since the previous transaction has not yet finished executing, the victim contract has not yet reached the line of code where the attacker's balance remains. The contract sees that the attacker's balance is still intact and sends funds to the attacker again, repeating the process in an infinite loop until the victim contract runs out of reserves or the call stack becomes saturated.
The Checks-Effects-Interactions pattern
The ultimate defense against reentrant attacks lies in enforcing a strict ordering of function logic known as the Checks-Effects-Interactions design pattern:
- Checks: Check all required conditions (e.g.
require(balance >= withdrawAmount)). - Effects: Modify all states and internal variables of the contract before performing external operations (e.g. subtracting the user's balance:
balances[msg.sender] -= withdrawAmount). - Interactions: Make external calls to other accounts or fund transfers at the end (e.g.
payable(msg.sender).call{value: withdrawAmount}("")).
Following this pattern, if the attacker attempts to reenter the withdrawal function in the interaction phase, the victim contract will read its updated balance (which is already zero after the effects phase) and immediately reject the new request.
Mutual exclusion modifiers
Another fundamental layer of defense is the use of locks or nonReentrant modifiers provided by standard libraries such as OpenZeppelin. This modifier sets a lock boolean variable upon entering the function and releases it upon exit. If a recursive call to the same function is detected before it completes, the transaction automatically rolls back, ensuring logical immutability.


