Smart Contract Security

Implementing ReentrancyGuard: More Than Just a Modifier

Kennedy OwiroOctober 24, 20257 min read

OpenZeppelin's nonReentrant modifier is the most commonly used reentrancy protection. But slapping it on every function isn't always correct, and it's not always enough. Cross-contract reentrancy, read-only reentrancy, and gas-efficient alternatives with transient storage all require deeper understanding.

How ReentrancyGuard Works

// OpenZeppelin's implementation (simplified)
abstract contract ReentrancyGuard {
    uint256 private _status;
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    modifier nonReentrant() {
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

It's a mutex lock: set a flag on entry, clear it on exit. If a reentrant call hits the flag, it reverts.

What ReentrancyGuard Doesn't Protect Against

1. Cross-Contract Reentrancy

Contract A has a guard. Contract B reads from A during A's callback. A's guard doesn't protect B's reads — B sees stale state.

2. Read-Only Reentrancy

A view function returns incorrect data during a callback because state hasn't been updated yet. The guard doesn't apply to view functions.

3. Cross-Function, Same Contract

If you only apply nonReentrant to withdraw() but not transfer(), an attacker can re-enter via transfer() during withdraw()'s callback.

EIP-1153: Gas-Efficient Guards with Transient Storage

// New in Solidity 0.8.24+ (Cancun upgrade)
// Transient storage is cleared after each transaction — perfect for guards
modifier nonReentrantTransient() {
    assembly {
        if tload(0) { revert(0, 0) }
        tstore(0, 1)
    }
    _;
    assembly {
        tstore(0, 0)
    }
}
// Gas cost: ~100 gas vs ~5,000 for SSTORE-based guard

Best Practices

  • ✅ Apply nonReentrant to ALL external functions that modify state, not just those with ETH transfers
  • ✅ Use CEI pattern even WITH a guard — belt and suspenders
  • ✅ For cross-contract scenarios, share the guard state or use a global lock
  • ✅ Consider EIP-1153 transient storage for gas-efficient guards (Cancun+)
  • ✅ Guard view functions that return sensitive state during callbacks

Reentrancy guards are necessary but not sufficient. A full audit catches the reentrancy paths that a simple modifier misses.

reentrancyReentrancyGuardnonReentrantEIP-1153transient storageSolidity
Share

Written by

Kennedy Owiro

Founder & CTO, Vultbase

14+ years building security and QA systems at scale. Background in fintech security and Web3 smart contract testing. Built Vultbase's Intelligence Engine with 1,200+ exploit patterns from $40B+ in historical DeFi losses.

Protect your protocol before launch.

Submit your smart contracts for automated security analysis powered by 1,200+ real exploit patterns.

Start Your Audit →