Free preview10 min read

Reentrancy: The Oldest Trick That Still Works

A contract that sends money before it updates its own records can be asked for that money again, mid-send. That is reentrancy, the flaw behind the first famous disaster in Ethereum's history, and it is still finding victims.

The mechanism

  1. A contract holds balances for depositors and offers a withdraw function.
  2. Withdraw sends the funds first, then sets the depositor's balance to zero.
  3. Sending funds to another contract hands control to that contract's code.
  4. That code calls withdraw again. The balance has not been zeroed yet, so the check passes a second time.
  5. The loop repeats until the pool is empty.

The fix is an ordering rule known as checks, effects, interactions: validate first, update your own state second, talk to anyone else last. A reentrancy guard, a lock that refuses a second entry while the first call is open, is the belt to that pair of braces.

Where it has happened

  • The DAO, June 2016. A reentrancy flaw drained about 3.6 million ETH from an investment fund built on Ethereum. The response split the chain: the majority forked to reverse the theft, and the chain that refused continues today as Ethereum Classic.
  • Curve Finance, July 2023. The flaw was not in Curve's own logic but in specific versions of the Vyper compiler, where the reentrancy guard did not work as written. Several pools were drained, with losses reported around 70 million dollars. Audited code can still be undone by the tools that compile it.

Why it survives

  • Any external call can hand over control: token transfer hooks, callbacks, and newer token standards that notify the receiver.
  • Read-only reentrancy is subtler. An attacker re-enters a view function while state is halfway updated, so another protocol reads a wrong price or balance and acts on it.
  • Cross-function reentrancy uses a different function that shares the same state, so a guard on one function alone is not enough.

What actually prevents it

  • Checks, effects, interactions, applied everywhere, not just in the obvious withdraw.
  • Reentrancy guards on every state-changing entry point that makes external calls.
  • Treating every token as hostile code, because a token contract runs its own logic during a transfer.
  • Tests that call back into the contract from a malicious receiver, which is how this is found before launch.
Education, not financial advice. Nothing here is a recommendation to use or avoid any protocol.