The Transfer That Never Stopped
By Day 30 of this Web3 journey, “security” stopped being a scary audit word and started feeling very real.
Because the first time an “innocent” withdraw function drained an entire contract, it didn’t look like a hack at all. It looked like… a normal payment going through.
The user clicked “withdraw.” The contract sent them some ETH. Everything looked fine. Except the payment never really ended. It kept calling back. And back. And back again.
By the time the contract “realized” what happened, its balance was gone.
That’s reentrancy — and with AI helping us write Solidity faster than ever, it’s now dangerously easy to ship this bug by accident
What Reentrancy Really Is
Think of a contract like a vending machine:
- You insert a coin.
- The machine checks your balance.
- Then it updates the balance and drops your snack.
Now imagine a bug where, while the snack door is still open, you can hit the “dispense” button again and again before the machine updates your balance.
You keep grabbing snacks while the machine still thinks you’ve only taken one.
That’s reentrancy in Web3 terms:
- A smart contract sends ETH or tokens to another address or contract.
- While that transfer is happening, the receiver runs code.
- That code calls back into the original contract’s function before the balance or state is updated.
- The attacker repeats this loop, draining funds.[2][1]
Key idea:
The contract trusts that the external call is “just a transfer.”
But in Ethereum, that recipient can be a contract with its own logic.
The Classic Reentrancy Trap (And Why AI Loves It)
Here’s the vulnerable pattern AI tools often generate when you ask for a “simple withdraw function”:
- Check if
msg.senderhas enough balance. -
callorsendETH tomsg.sender. - After sending, update
balances[msg.sender] = 0.
This looks perfectly logical in plain English.
But the order of operations is deadly:
- External call goes out (to an attacker contract).
- Attacker’s fallback function runs and calls
withdraw()again. - The original contract still thinks the attacker has balance, because it hasn’t set it to 0 yet.
- Funds keep flowing until the contract is empty.
AI models often:
- Use
call{value: ...}("")without mentioning reentrancy risks. - Forget to use a reentrancy guard or checks-effects-interactions pattern.
- Prioritize “working example” over “secure example,” especially when you ask for “simple”, “minimal”, or “gas efficient” code.
The code compiles.
The tests pass.
Mainnet doesn’t forgive.
Checks-Effects-Interactions: Your First Shield
To fight reentrancy, you flip the vending machine logic:
Instead of: “Send snack → then update balance”
You do: “Update balance → then send snack”
In Solidity, this is called checks-effects-interactions:
- Checks: Validate conditions (require statements).
- Effects: Update internal state (balances, mappings).
- Interactions: Call external contracts or send ETH last.
Why this works:
- When the attacker tries to re-enter, your contract’s state already says: “You have no balance.”
- So even if they call back in, your
requirefails.
Many AI-generated examples follow this sometimes, but not consistently.
Any time you see:
-
call,delegatecall, ortransfer - followed by a state change
…your “reentrancy radar” should beep.
Reentrancy Guards & Safe Patterns
Modern Solidity has extra safety nets you should almost always use:
- ReentrancyGuard: A simple “locked” flag that blocks a function from being entered again before it finishes.
- Pull over push payments: Instead of sending ETH automatically, let users withdraw in a controlled, minimal function.
- Minimize external calls: The fewer external calls, the fewer reentrancy surfaces.[4][1][3]
Think of ReentrancyGuard like putting a “Busy” sign on the vending machine door:
- While one snack is being dispensed, no one else can press buttons.
- Including the same person trying to spam the button through a backdoor.
As a beginner (and especially when using AI):
- Default to using
ReentrancyGuardon any function that sends ETH or calls untrusted contracts. - Only remove it when you fully understand why it’s safe without it.
Common AI‑Generated Security Mistakes (Beyond Reentrancy)
Reentrancy is just one category. AI tends to repeat a few dangerous patterns:
-
Missing access control
- Admin-only functions (like
setPrice,pause,withdrawAll) are left aspublic. - Anyone can call them.
- Admin-only functions (like
-
Blind trust in
msg.sender- No role checks.
- No
onlyOwnerorAccessControl.
-
Unbounded loops over arrays
- “Simple” loops over large user lists that can run out of gas.
- Perfect for griefing or DoS.[4][3]
-
Ignoring return values
- Not checking if token
transferorcallactually succeeded.
- Not checking if token
All of these look reasonable at first glance.
That’s what makes them dangerous: they fail not in the compiler, but in production.
Your job as a beginner isn’t to write “perfect” code.
It’s to notice when something could go wrong, and slow down there.
Why This Matters For Developers
Reentrancy is not “just another bug.”
It’s the kind of bug that:
- Empties treasuries.
- Breaks user trust.
- Follows your name around on-chain forever.
Think About This (Mini Challenge)
Next time you ask an AI tool:
“Write a simple Solidity contract that lets users deposit and withdraw ETH.”
Do this:
- Before you run it, scan for external calls (
call,transfer,send). - Check: “Does this function update state before or after sending ETH?”
- Ask yourself: “What happens if the receiver is a contract that calls back in right here?”
If you feel even a tiny bit of doubt, good.
That’s your security instinct waking up.
Key Takeaway
Reentrancy isn’t magic.
It’s just “I trusted external code before I locked my own house.”
As AI starts writing more of our Solidity, your edge won’t be typing faster.
It will be spotting the trapdoors AI leaves behind.
Don’t aim to be the smartest auditor in the room.
Aim to be the developer who never ships the obvious bug.
Because in Web3, one “innocent” withdraw function can be the difference between:
- “Nice little dApp you deployed.”
- and
- “Remember that contract that got drained? Yeah… that was mine.”
Resources to Go Deeper
Solidity Docs — Security Considerations (Reentrancy section)
Official language docs explaining why external calls are dangerous and how to structure state changes safely. Great to anchor your explanations in the source spec.ConsenSys Diligence — Reentrancy (Smart Contract Best Practices)
Classic reference for the attack pattern, checks‑effects‑interactions, and common pitfalls; widely recognized in the Ethereum security world.OpenZeppelin Contracts — ReentrancyGuard
The de‑facto standard implementation of a reentrancy lock; perfect follow‑up for readers who want to actually use the pattern you describe.
- Follow the series on Medium | Twitter | Future
- Jump into Web3ForHumans on Telegram and we’ll brainstorm Web3 together.
Top comments (0)