Walk me through how a reentrancy vulnerability happens in a Solidity contract and how you'd defend against it in current practice.
technical-conceptual · Mid level · software-engineering, blockchain-web3
What the interviewer is really asking
Assesses practical secure-coding judgment: whether the candidate understands the root cause (state mutated after an external call) and applies layered, current defenses rather than reciting one buzzword.
What to say
- Name the root cause: an external call (e.g., sending ETH) made before the contract updates its own state, letting the callee re-enter the function while the old state still says funds are available.
- Lead with the checks-effects-interactions pattern — validate inputs, then write all state changes, and only then make the external call — as the primary structural defense.
- Add a reentrancy guard (e.g., OpenZeppelin's nonReentrant, increasingly backed by transient storage / EIP-1153 for a cheaper lock) as defense-in-depth, and mention cross-function and read-only reentrancy as variants a single lock may miss.
What to avoid
- Naming only the nonReentrant modifier with no understanding of checks-effects-interactions or why ordering matters.
- Claiming you'd 'just avoid external calls' — many contracts must transfer value or call other contracts.
- Asserting transfer/send caps the gas so reentrancy is impossible — that's outdated guidance and breaks with smart-contract wallets and changing opcode costs.
Example answers
Strong: Reentrancy happens when you make an external call before updating state — the classic is sending ETH in a withdraw before zeroing the user's balance, so the recipient's fallback calls withdraw again while the balance still looks full. My primary fix is checks-effects-interactions: check, then zero the balance, then transfer. On top of that I add a nonReentrant guard for defense-in-depth — modern ones use transient storage so the lock is cheap — and I think about cross-function and read-only reentrancy, because a single function's guard won't protect a view another contract reads mid-call.
Weak: You add the nonReentrant modifier from OpenZeppelin to the function and that stops reentrancy. As long as you use transfer instead of call it's also safe because transfer only forwards 2300 gas.