What is the technical principle of reentrancy attacks, and why is the order of contract operations so important? Reentrancy's root vulnerability lies in a smart contract updating its own ledger after transferring funds — this operation order creates exploitable opportunity. The correct approach: update the ledger first (set user balance to 0), then transfer funds to the user. The vulnerable pattern: transfer funds to the user, then update the ledger after transfer completes. The problem: Ethereum's .call() method, when paying a contract, can trigger the receiving contract's fallback function the instant ETH is received. The attacker's fallback function calls the original contract's withdraw again — at this point the original contract's ledger hasn't updated, so the balance check still passes. This re-entry can repeat until the contract's ETH is drained. The entire vulnerability's logic is the wrong order of transfer-then-record.
What was The DAO attack and why was it so important? The DAO (Decentralized Autonomous Organization) was a 2016 Ethereum crowdfunding smart contract project that raised approximately $150 million worth of ETH at the time. In June 2016, an attacker exploited a reentrancy vulnerability in The DAO's withdrawal contract, repeatedly withdrawing and ultimately transferring away approximately 3.6 million ETH (then worth ~$60 million). The event's impact far exceeded an ordinary hack: the Ethereum community faced a fundamental choice — should they hard fork (modify blockchain state) to forcibly return the stolen ETH to DAO investors? Ultimately, the Ethereum community voted by majority to hard fork, rolling Ethereum back to the state before the attack and returning the stolen funds. This is the origin of today's Ethereum (ETH); the minority who refused to recognize the hard fork maintained the original chain — this became Ethereum Classic (ETC). The DAO attack made the entire crypto industry deeply understand the importance of smart contract security, and established the foundational debate between blockchain immutability and governance emergency response.
How do you defend against reentrancy attacks? What verified patterns exist? Several standard defensive approaches. First, Checks-Effects-Interactions (CEI) pattern: the most fundamental and important defensive principle. Any contract function should execute in this order: Checks (do all condition checks first) → Effects (update all internal contract state) → Interactions (only then interact with external contracts or addresses, like transferring funds). With state updates before the transfer, even if an attacker re-enters, the balance is already 0, the check fails, and the attack is neutralized. Second, ReentrancyGuard (mutex lock): OpenZeppelin provides a standard ReentrancyGuard module — essentially a boolean mutex lock that sets to locked during function execution and unlocks on completion; if re-entered while locked, it reverts immediately. Simple and effective for complex function call chains. Third, avoid raw call() for payments: transfer() and send() methods forward only 2,300 Gas by default — insufficient for a complex-logic fallback to execute, structurally limiting reentrancy viability (but shouldn't be the primary defense, as Gas mechanics may change with upgrades).
Beyond reentrancy attacks, what other common smart contract vulnerability types exist? A few equally important smart contract vulnerability types to know. Integer overflow/underflow: before Solidity 0.8's automatic overflow protection, numeric calculations could wrap around (e.g., uint8's 255 + 1 = 0) — exploited to fabricate balances. Modern Solidity has protection, but old contracts remain at risk. Unsafe external calls: when calling external contracts, incorrect handling of return values can let attackers control execution flow. Logic errors: not technical vulnerabilities but business logic design errors — reversed conditionals, missing permission controls — letting attackers use the contract in unintended ways. Flash loan attacks: using flash loans' instant massive liquidity to manipulate market or oracle prices within a single transaction, affecting lending or liquidation logic that relies on these prices. Understanding these vulnerabilities is foundational knowledge for evaluating DeFi protocol security.
Use pseudocode to explain the complete reentrancy attack flow. Imagine a simple Ethereum deposit/withdrawal contract with a balances mapping tracking each address's balance. The vulnerable withdraw function:
function withdraw() external {
require(balances[msg.sender] > 0); // check balance
(bool success,) = msg.sender.call{value: balances[msg.sender]}(""); // send funds FIRST
require(success);
balances[msg.sender] = 0; // update balance AFTER
}
The attacker deploys a malicious contract with a fallback function:
fallback() external payable {
if (address(victim).balance > 1 ether) {
victim.withdraw(); // immediately call again the instant ETH is received
}
}
Attack flow: attacker calls victim.withdraw(); contract checks balance > 0 (passes); contract transfers ETH to attacker; attacker's fallback triggers and immediately calls victim.withdraw() again; at this point the contract's balances[attacker] is still the original value (update happens after transfer); check passes again, transfer again; loop until contract is empty.
The CEI-fixed version: update balances[msg.sender] = 0 first, then .call{value: ...}. One line of order change — the entire attack is blocked.
Reentrancy reveals a fundamental engineering trade-off in smart contract design: the sequence between external calls (transferring funds to another address or contract) and state updates. The intuitive write-then-record approach makes business logic clearer (give you the money first, then note you have none), but introduces reentrancy risk. The CEI record-then-write approach, while slightly counterintuitive for business logic, completely eliminates the reentrancy problem. This trade-off now has a clear consensus answer — always use CEI — but it reminds us that smart contract code security and intuitive business logic sometimes have subtle conflicts. Every smart contract is public and immutable — once deployed with a bug, fixing it requires migrating to a new contract, not a silent patch push like traditional software. This makes pre-launch auditing of smart contracts more critical than any traditional software.