SEO TITLE
- Reentrancy in Smart Contracts: How It Works and How to Prevent It
- Reentrancy Explained: The Smart Contract Vulnerability Every Developer Should Know
- Reentrancy Attacks: A Practical Guide for Smart Contract Security
META TITLE
Reentrancy in Smart Contracts Explained
META DESCRIPTION
Learn what reentrancy is, how reentrancy attacks work, and how to secure smart contracts with practical examples and best practices.
URL SLUG
reentrancy
CONTENT SUMMARY
This tutorial explains reentrancy in smart contracts from first principles to real-world security practice. It is designed for developers, security professionals, enterprises, and advanced learners who need a clear, practical understanding of how reentrancy happens, why it matters, and how to defend against it.
ARTICLE
Introduction
Reentrancy is one of the most important security concepts in smart contract development because it turns a simple mistake in execution order into a serious fund-loss risk.
In plain English, reentrancy happens when a smart contract gives control to another contract before finishing its own internal bookkeeping, and that external contract calls back in before the first function has completed. If the original contract state has not been updated yet, the attacker may be able to repeat an action like withdrawing funds multiple times in a single transaction.
This matters now because modern blockchain contract systems are highly composable. A single contract interaction may involve token transfers, vaults, lending protocols, oracle integration, proxy contract logic, and on-chain automation. That composability is powerful, but it also creates more places where an external contract call can re-enter execution.
In this guide, you will learn what reentrancy is, how it works technically, the main variants, where it shows up in real protocols, and the best ways to prevent it in production smart contract systems.
What is reentrancy?
Beginner-friendly definition
Reentrancy is when a smart contract is called again before its previous execution finishes.
A common example is a vulnerable withdrawal function:
- The contract checks your balance.
- It sends you funds.
- Before it updates your balance to zero, your contract calls the withdrawal function again.
- The contract still thinks your balance is available, so it sends funds again.
That is the core idea.
Technical definition
Technically, reentrancy occurs when a contract function makes an external call before finalizing its own contract state in storage, and the callee re-enters the caller through another contract call during the same execution flow.
In EVM terms, contract A transfers control to contract B through call, an interface invocation, a token hook, or another external interaction. Contract B then invokes a function on contract A again before contract A completes its original execution path.
Why it matters in the broader Smart Contracts ecosystem
A smart contract is a self-executing, programmable contract whose contract bytecode runs at a contract address on a blockchain. Because these systems are designed to support decentralized contract composition, a single contract function may interact with many other contracts through a shared contract ABI.
That is powerful for DeFi, programmable escrow, self-custody automation, and automated contract systems. But the same design also means one unsafe external call can expose partially updated contract storage to untrusted code.
Reentrancy matters even more because:
- it can affect native coin transfers and token-based systems
- it can happen across multiple contract functions, not just one
- it can appear in upgradeable contract architectures and proxy contract patterns
- immutable contract deployments are hard to patch after launch
How reentrancy Works
The easiest way to understand reentrancy is to see the vulnerable pattern: interaction before state update.
Step-by-step explanation
Imagine a vault contract that lets users deposit and withdraw funds.
- A user deposits funds into the vault.
- The vault records the amount in contract storage.
- The user calls
withdraw(). - The vault reads the stored balance.
- The vault sends funds to the caller using an external call.
- The caller is a malicious contract, so its fallback or receive function runs.
- That fallback calls
withdraw()again before the first call finishes. - Because the vault has not yet updated the stored balance, the second call also succeeds.
- This can repeat until the vault is drained or the transaction fails.
Simple vulnerable example
contract VulnerableVault {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// External call before state update
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "Transfer failed");
balances[msg.sender] = 0;
}
}
The problem is not the transfer itself. The problem is that the contract call happens before the balance is cleared.
Safer version
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SafeVault is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external nonReentrant {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// Effects first
balances[msg.sender] = 0;
// Interaction after state update
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "Transfer failed");
}
}
This version improves security in two ways:
- it updates contract state before the external interaction
- it uses a reentrancy lock
Technical workflow
At a deeper level, reentrancy depends on control flow, not just money movement.
Reentrancy can happen when:
- a contract sends native currency with a low-level call
- a token standard triggers hooks or callbacks
- one external function calls another contract that calls back
- a protocol reads a value during an inconsistent intermediate state
- a proxy contract routes execution into logic that exposes shared storage paths
This is why reentrancy analysis is not just about “withdraw ETH.” It is about any external contract interaction that happens before state is finalized.
Key Features of reentrancy
Reentrancy is best understood through the characteristics that make it dangerous:
-
Control is handed to untrusted code.
The moment a smart contract performs an external call, execution is no longer fully under its control. -
Partially updated contract state is exposed.
If storage changes are delayed, the callee may observe or exploit stale balances, share counts, permissions, or accounting variables. -
It happens inside one transaction.
Reentrancy is not mainly about transaction ordering on the mempool. It is about re-entering execution before the original call frame finishes. -
It is broader than a single function.
A contract may guard one function but still be vulnerable through another contract function that touches the same storage. -
Composability increases the surface area.
Modern decentralized contract systems interact with tokens, routers, vaults, bridges, automation services, and oracles. Every external dependency deserves scrutiny. -
It can become a system-level problem.
In DeFi, one reentrancy flaw can affect liquidity pools, pricing, collateral accounting, or downstream integrations.
Types / Variants / Related Concepts
Not every reentrancy issue looks the same.
Single-function reentrancy
This is the classic case. The attacker re-enters the same contract function before state is updated.
Example: withdraw() calling back into withdraw().
Cross-function reentrancy
The attacker does not need to call the same function again. They may re-enter through a different contract function that relies on the same state.
Example: a claimReward() path and a withdrawStake() path both depend on the same balance accounting.
Cross-contract reentrancy
The vulnerable logic may span multiple contracts. One contract updates state, calls another module, and that module calls back into a related contract sharing assumptions or storage-linked behavior.
This is especially relevant in modular DeFi systems and enterprise blockchain contract stacks.
Read-only reentrancy
This is subtler. The reentrant call does not directly mutate the vulnerable contract, but it reads a value while state is inconsistent and uses that stale or manipulated value somewhere else.
Examples include:
- vault share pricing
- collateral ratios
- oracle-dependent checks
- accounting snapshots used by another contract
Token hook and callback reentrancy
Some token standards and transfer patterns can execute code on receipt or during transfer handling. That means a token transfer may be an external interaction, not just a local balance move.
A common mistake is treating all token operations as safe internal accounting. They are not.
Reentrancy and upgradeable contracts
Upgradeable contract systems often rely on a proxy contract that delegates calls to implementation logic. This does not remove reentrancy risk. In some cases it makes review harder because:
- storage layout must remain correct
- guards must be initialized correctly
- multiple implementations may share assumptions
- admin upgrade flows need strong access control
Related concepts that people confuse with reentrancy
- Recursion: a programming pattern where a function calls itself by design. Reentrancy is usually an unintended security issue involving external control flow.
- Front-running / MEV: transaction ordering exploitation. Different problem.
- Access control flaws: unauthorized execution due to bad permissions. Different root cause, though both can coexist.
- Gas optimization: reducing gas is useful, but unsafe micro-optimizations can worsen reentrancy exposure.
Benefits and Advantages
Reentrancy itself is not a feature you want. The benefit comes from understanding it and designing around it.
Teams that build with reentrancy in mind gain several advantages:
-
Safer smart contract architecture
You design explicit state transitions instead of relying on hopeful assumptions. -
Better auditability
A clean separation between checks, effects, and interactions makes contract audit work more reliable and faster. -
More resilient composability
Your contract can interact with tokens, vaults, oracles, and automation systems with fewer hidden assumptions. -
Stronger enterprise controls
For business deployments, safer contract interaction patterns reduce operational and treasury risk. -
More trustworthy self-custody systems
Users may protect private keys with strong wallet security and digital signatures, but that does not protect them from flawed protocol logic. Reentrancy-aware design closes that gap.
Risks, Challenges, or Limitations
Reentrancy is dangerous because it often hides inside normal-looking code.
Main risks
- drained funds from vaults, escrow contracts, treasuries, or reward pools
- broken accounting for balances, shares, debt, or collateral
- inconsistent event log records that complicate incident response
- cascading failures in integrated DeFi protocols
- loss of user trust even if private keys were never compromised
Why it remains hard
-
External calls are everywhere.
Token transfers, callbacks, oracle updates, router interactions, and automated contract execution all widen the attack surface. -
Some attacks are indirect.
A contract may appear safe until a downstream dependency changes behavior. -
Upgradeable systems add complexity.
An upgradeable contract can be patched, but proxy patterns introduce more logic paths and access control requirements. -
Immutable contracts are hard to fix.
If vulnerable bytecode is already deployed and cannot be upgraded, mitigation options may be limited to pausing connected systems or migrating funds. -
“View” assumptions can be wrong.
Read-only reentrancy shows that even observation of state can be dangerous if done mid-execution.
Real-World Use Cases
Understanding reentrancy is practical anywhere smart contracts move value or depend on external logic.
1. DeFi vault withdrawals
Vaults that hold user funds and support deposits, redemptions, or yield harvesting are classic reentrancy targets.
2. Lending and collateral systems
A reentrant path can interfere with borrow, repay, liquidation, or collateral accounting if state changes are ordered incorrectly.
3. Token standards with hooks
Certain token flows can trigger recipient logic. Protocols must treat those transfers as external contract interaction.
4. NFT marketplaces and royalty payouts
If a marketplace sends proceeds before updating order state, a malicious receiver contract may re-enter and claim more than intended.
5. Programmable escrow
Escrow contracts that release funds based on milestones, signatures, or oracle integration must finalize state before payout logic.
6. DAO treasury modules
Treasury contracts that interact with plugins, payment adapters, or governance execution modules can expose cross-contract reentrancy paths.
7. Self-custody automation
Smart accounts, recurring payment modules, and automated asset management systems need reentrancy-safe execution because user-approved signatures do not make the target logic safe.
8. On-chain automation and keepers
Automation bots that trigger rebalancing, harvesting, or settlement can hit complex external call chains. If those paths are not reentrancy-safe, scheduled operations may become exploit vectors.
reentrancy vs Similar Terms
| Term | What it means | Main cause | Typical impact | How it differs from reentrancy |
|---|---|---|---|---|
| Reentrancy | A contract is called again before previous execution finishes | External call before state is finalized | Drained funds, broken accounting | Core issue is unexpected callback into incomplete execution |
| Recursion | A function calls itself intentionally | Program logic | Can be valid or buggy | Not necessarily a security flaw and does not require external control |
| Front-running / MEV | Someone exploits transaction ordering | Mempool visibility and ordering power | Worse prices, failed trades, unfair execution | Happens across transaction ordering, not mid-execution callback |
| Access control flaw | Unauthorized user or contract can call privileged logic | Weak authentication or bad permission checks | Admin abuse, upgrades, theft | Root problem is authorization, not re-entry |
| Integer overflow/underflow | Arithmetic exceeds numeric bounds | Unsafe math assumptions | Wrong balances or calculations | Math bug, not control-flow bug |
Best Practices / Security Considerations
If you write or review smart contracts, these are the habits that matter most.
1. Use Checks-Effects-Interactions
Do validation first, update internal state second, and perform external calls last.
This is still one of the best baseline defenses.
2. Use a reentrancy guard where appropriate
A lock such as nonReentrant can block nested entry into protected functions. But do not assume it solves everything. You must review all public and external contract functions that touch shared state.
3. Prefer pull payments over push payments
Instead of automatically sending funds during many workflows, record the amount owed and let users claim it in a dedicated withdrawal path.
This reduces the number of risky external interactions.
4. Treat token transfers as external interactions
Do not assume token transfer logic is harmless. If a token standard, wrapper, or adapter can trigger callbacks, it belongs in the threat model.
5. Minimize external calls in sensitive code
Separate accounting from integration where possible. A contract function that updates balances and a contract function that performs integrations may be safer as distinct phases.
6. Design explicit state machines
For complex protocols, use clear state transitions such as pending, finalized, claimed, canceled, or settled. This makes it harder for reentrant calls to exploit ambiguous intermediate states.
7. Test against malicious contracts
Unit tests should include attacker contracts that:
- re-enter the same function
- re-enter a different function
- attempt callback through token receipt hooks
- query view functions during intermediate state
Fuzzing and invariant testing are especially useful here.
8. Be careful with proxy and upgrade flows
For an upgradeable contract, verify:
- the reentrancy guard is initialized properly
- storage layout remains safe
- implementation upgrades do not reopen old assumptions
- admin paths have strict access control
9. Do not rely on transfer or send as a security boundary
Older advice often treated fixed-gas transfers as a reentrancy defense. That is not a robust modern security strategy. Design for safety explicitly.
10. Verify and monitor after contract deployment
Contract verification, ABI review, runtime monitoring, and suspicious event log analysis help teams detect abnormal patterns early, even though they do not replace preventive design.
Common Mistakes and Misconceptions
“Reentrancy only affects Ether transfers”
False. Any external call can create the condition, including token interactions, callbacks, and multi-contract flows.
“Using nonReentrant everywhere makes the contract safe”
Not necessarily. Cross-function and cross-contract cases can still matter, and poor architecture can remain exploitable.
“View functions cannot be part of reentrancy issues”
False. Read-only reentrancy can exploit inconsistent observations during execution.
“If a contract is audited, reentrancy is no longer a concern”
A contract audit is valuable, but not a guarantee. Upgrades, integrations, and changed assumptions can introduce new paths later.
“Immutable contract means safer”
Not automatically. Immutable code removes upgrade risk, but it also removes easy patching if a bug exists.
“Wallet security prevents reentrancy losses”
Private key management, authentication, and digital signatures protect account control. They do not protect you from flawed smart contract logic after you approve an interaction.
Who Should Care About reentrancy?
Developers
If you write Solidity or review contract bytecode, reentrancy should be a first-class design concern from the start.
Security professionals
Auditors, protocol reviewers, and incident responders need to evaluate contract interaction graphs, shared storage assumptions, and callback surfaces.
Businesses and enterprises
If your organization uses automated contract systems for escrow, tokenization, treasury flows, or settlement, reentrancy risk belongs in architecture and governance review.
Investors and DeFi users
If you deposit assets into protocols, reentrancy matters even when your self-custody wallet is secure. Protocol logic risk can still affect your funds.
Advanced learners
Reentrancy is foundational knowledge for understanding smart contract security, DeFi architecture, and protocol design.
Future Trends and Outlook
Reentrancy is unlikely to disappear because composability is a core feature of smart contract platforms.
What is changing is the quality of defense:
- better default libraries and guard patterns
- stronger static analysis and fuzzing tools
- more invariant-based testing in development pipelines
- improved awareness of read-only and cross-function reentrancy
- more disciplined review of upgradeable contract and proxy contract systems
At the same time, protocols are becoming more modular. Oracle integration, account abstraction, automation layers, and plugin-based architectures increase the number of external call paths. That means security teams will need to think less about isolated functions and more about end-to-end execution graphs.
The long-term lesson is simple: secure smart contract design is not just about writing correct functions. It is about controlling when and how execution leaves your contract.
Conclusion
Reentrancy is a classic smart contract vulnerability because it exploits a basic truth of blockchain programming: once your contract makes an external call, control flow may no longer be yours.
If you remember one rule, make it this: update internal state before external interaction, and treat every external call as potentially hostile.
For developers, that means using strong patterns like Checks-Effects-Interactions, pull payments, reentrancy guards, and adversarial testing. For businesses and users, it means looking beyond wallet security and asking whether the protocol itself was designed and audited for safe contract interaction.
FAQ SECTION
1. What is reentrancy in simple terms?
Reentrancy is when a smart contract gets called again before it finishes its current task, often letting an attacker repeat an action like withdrawing funds multiple times.
2. Is reentrancy always an attack?
No. Reentrancy is a control-flow behavior. It becomes a vulnerability when the contract is not designed to handle it safely.
3. What causes reentrancy in Solidity?
Usually, an external call is made before internal state is updated. This lets the callee call back into the contract while storage still reflects the old state.
4. Can ERC-20 token transfers cause reentrancy?
Sometimes related token flows can, especially if wrappers, adapters, hooks, or non-standard token behavior are involved. Treat external token interactions as potentially risky.
5. What is read-only reentrancy?
It is when a reentrant call reads inconsistent intermediate state and uses that value elsewhere, even if the vulnerable contract is not directly mutated again.
6. Does nonReentrant solve all reentrancy issues?
No. It helps, but it does not replace sound architecture, proper state ordering, and review of all related functions and contracts.
7. Is using transfer() enough to prevent reentrancy?
No. It should not be treated as a modern security boundary. Use explicit defensive design instead.
8. How do auditors detect reentrancy?
They review external call sites, shared storage dependencies, ABI-exposed entry points, fallback behaviors, token hooks, and test malicious callback scenarios.
9. Are upgradeable contracts more vulnerable to reentrancy?
Not automatically, but they are more complex. Proxy contract setups require careful storage layout, initialization, and access control review.
10. Why should regular users care about reentrancy?
Because your wallet can be perfectly secure while the protocol you interact with is not. Smart contract risk is separate from private key safety.
KEY TAKEAWAYS
- Reentrancy happens when a contract is called again before its first execution path finishes.
- The classic bug is making an external call before updating contract storage.
- Reentrancy is not limited to one withdrawal function; it can be cross-function, cross-contract, or read-only.
- Token hooks, external integrations, oracle-driven logic, and automation can all expand the attack surface.
- Checks-Effects-Interactions is still a foundational defense.
- Reentrancy guards help, but they do not replace sound protocol design.
- Pull-payment architectures are often safer than pushing funds during complex workflows.
- Upgradeable and proxy-based systems need extra review because complexity hides assumptions.
- Audits are important, but continuous testing, monitoring, and verification still matter.
- Wallet security protects keys, not flawed smart contract logic.
INTERNAL LINKING IDEAS
- Smart Contract Audit Checklist for DeFi Teams
- Proxy Contract Explained: How Upgradeable Contracts Work
- Checks-Effects-Interactions in Solidity
- Contract ABI Explained for Developers and Auditors
- Access Control in Smart Contracts: Roles, Ownership, and Risks
- Contract Verification: Why Verified Source Code Matters
- Gas Optimization vs Security in Solidity
- Oracle Integration Risks in Smart Contracts
- Event Logs in Ethereum: How to Track Contract Activity
- Programmable Escrow Design Patterns on Blockchain
EXTERNAL SOURCE PLACEHOLDERS
- official Solidity documentation
- official Ethereum developer documentation
- OpenZeppelin security and contract library documentation
- token standard specifications and standards body materials
- smart contract security audit reports
- incident postmortems from affected protocols
- academic papers on smart contract vulnerabilities and formal verification
- blockchain explorer pages for verified contract code and transaction traces
- static analysis and fuzzing tool documentation
- enterprise or regulatory guidance for digital asset risk management, verify with current source
IMAGE / VISUAL IDEAS
- Flowchart showing a normal withdrawal vs a reentrant withdrawal
- Annotated Solidity code snippet of vulnerable and fixed contracts
- Diagram of call stack: Contract A → Contract B → Contract A
- Visual comparison of single-function, cross-function, and read-only reentrancy
- Smart contract security checklist graphic for deployment and audit review
SCHEMA SUGGESTIONS
- Article
- TechArticle
- FAQPage
- HowTo
- Glossary