If you are a web developer, a bug means a 500 error page.
If you are a smart contract developer, a bug means the money is gone. Forever.
The scariest bug in the book is the Reentrancy Attack. It’s the vulnerability that destroyed The DAO in 2016 and keeps draining protocols in 2026.
I remember auditing a contract I wrote for a client. I looked at the "Withdraw" function, and my stomach dropped. I had left the door wide open.
Here is exactly how Reentrancy works, and the three patterns I use to lock my contracts down.
The "Infinite ATM" Glitch
Imagine you go to an ATM.
You insert your card and ask for $100.
The ATM gives you $100.
Then the ATM checks your balance and subtracts $100.
Now, imagine if you were fast enough to ask for another $100 before the ATM could subtract the first one. You could drain the machine.
That is Reentrancy. A malicious contract calls your function, and before your function finishes updating the balance, the attacker calls it again. And again. Until the funds are zero.
Defense 1: The "Checks-Effects-Interactions" Pattern
This is the Golden Rule of Solidity. You must memorize this.
Most juniors write code like this (Vulnerable):
Check: Does user have funds?
Interact: Send money to user.
Effect: Subtract funds from balance.
The Fix: Switch the order.
Check: Does user have funds?
Effect: Subtract funds from balance FIRST.
Interact: Send money to user.
By updating the balance before sending the money, even if they try to re-enter, their balance is already zero. The attack fails.
Defense 2: The Mutex (ReentrancyGuard)
Sometimes, logic gets complex, and reordering lines isn't enough. That’s when I use a Mutex (Mutual Exclusion).
I use OpenZeppelin’s ReentrancyGuard. It works like a lock on a bathroom door.
When a function starts, the nonReentrant modifier locks the door.
If anyone tries to call the function again while the door is locked, the transaction reverts.
When the function finishes, it unlocks the door.
It costs a little extra gas, but it’s cheaper than losing $1 million.
Defense 3: Pull vs. Push
Stop sending ETH to users automatically.
Instead of pushing funds to them (which triggers code execution), let them pull the funds themselves.
Create a pending balance for them and force them to call a separate withdraw() function. This isolates the dangerous external call into its own specific transaction, making it much harder to exploit reentrancy loops in your main logic.
The Takeaway
Smart contract security isn't about being a genius mathematician. It’s about paranoia.
Whenever you write a line of code that sends tokens or ETH, stop. Stare at it. Ask yourself: "What happens if the person receiving this money is malicious?"
If you don't have an answer, don't deploy.
Hi, I'm Frank Oge. I build high-performance software and write about the tech that powers it. If you enjoyed this, check out more of my work at frankoge.com
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)