Mechanism of Re-Entrancy
When an Ethereum contract sends ETH to an external address using `call{value: amount}("")`, control is handed over to the recipient's `fallback()` or `receive()` function. If state variables (balances) are updated after the external call, the attacker can recursively call the withdraw function before balances are deducted.
The Checks-Effects-Interactions Pattern
Always enforce: 1. Checks (require statements, balance checks), 2. Effects (update internal state, deduct balances, mutate mappings), 3. Interactions (external contract calls and ETH transfers). Using OpenZeppelin's `ReentrancyGuard` or EIP-1153 transient storage (`TSTORE`) provides additional layers of defense.
// Vulnerable:
(bool s, ) = msg.sender.call{value: bal}("");
balances[msg.sender] = 0;
// Secure (CEI):
balances[msg.sender] = 0; // Effect
(bool s, ) = msg.sender.call{value: bal}(""); // Interaction
require(s, "Transfer failed");