.claude/skills/solidity-guard/skills/reentrancy-auditor/SKILL.md
Deep reentrancy vulnerability analysis for Solidity contracts. Covers single-function, cross-function, cross-contract, and read-only reentrancy. Maps all external call paths and validates CEI pattern compliance.
npx skillsauth add alt-research/solidityguard reentrancy-auditorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Detect all variants of reentrancy vulnerabilities in Solidity contracts. Reentrancy is the #1 cause of DeFi exploits historically, responsible for The DAO ($60M), Rari Capital ($80M), and many others.
State update occurs after external call within the same function.
// VULNERABLE
function withdraw(uint amount) external {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount; // STATE AFTER CALL
}
// SECURE (CEI Pattern)
function withdraw(uint amount) external nonReentrant {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // STATE BEFORE CALL
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
Multiple functions share state, and reentrancy through one corrupts another.
// VULNERABLE — attacker reenters transfer() during withdraw()
function withdraw(uint amount) external {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
balances[msg.sender] -= amount;
}
function transfer(address to, uint amount) external {
require(balances[msg.sender] >= amount); // Stale during reentrancy!
balances[msg.sender] -= amount;
balances[to] += amount;
}
External call in Contract A allows reentry into Contract B that reads A's stale state.
View functions return incorrect values during reentrancy, affecting other protocols.
// Contract A
function withdraw() external {
uint shares = balanceOf(msg.sender);
(bool s, ) = msg.sender.call{value: sharesToETH(shares)}("");
_burn(msg.sender, shares); // Burns AFTER call
}
// Contract B reads A's state during A's reentrancy
function getPrice() external view returns (uint) {
return contractA.totalAssets() / contractA.totalSupply(); // Wrong during reentrancy!
}
ERC-777 tokens call tokensReceived hook on recipient, allowing reentrancy.
Multiple contracts share the same transient storage slot via delegatecall, corrupting reentrancy guards.
// VULNERABLE — two libraries use same TSTORE slot
library LibA {
bytes32 constant LOCK_SLOT = 0x01;
function lock() internal { assembly { tstore(LOCK_SLOT, 1) } }
}
library LibB {
bytes32 constant LOCK_SLOT = 0x01; // COLLISION!
function lock() internal { assembly { tstore(LOCK_SLOT, 1) } }
}
// SECURE — use namespaced transient slots
library LibA {
bytes32 constant LOCK_SLOT = keccak256("LibA.reentrancy.lock");
function lock() internal { assembly { tstore(LOCK_SLOT, 1) } }
}
Reentrancy lock implemented via TSTORE can be bypassed if attacker enters with low gas that causes the TSTORE to fail silently or via cross-contract paths.
// VULNERABLE — TSTORE-based lock without TLOAD check
modifier nonReentrant() {
assembly {
if tload(0x00) { revert(0, 0) }
tstore(0x00, 1)
}
_;
assembly { tstore(0x00, 0) }
}
// Cross-contract call may bypass if callee uses delegatecall
// SECURE — use OpenZeppelin's ReentrancyGuardTransient
import "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
rg -n "\.call\{|\.call\(|\.transfer\(|\.send\(|\.delegatecall\(" contracts/
rg -n "safeTransfer|safeTransferFrom" contracts/
rg -n "IERC20\(.*\)\.(transfer|transferFrom)" contracts/
rg -n "tstore|tload|TSTORE|TLOAD" contracts/ # Transient storage reentrancy guards
nonReentrant modifier is applied# Find shared state variables
rg "mapping.*balances|mapping.*deposits|mapping.*shares" contracts/
# Then check all functions that read/write these
# Find view functions that calculate based on contract state
rg "function.*view.*returns" contracts/
# Check if these are used by external protocols
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract Vault is ReentrancyGuard {
function withdraw(uint amount) external nonReentrant {
// Protected
}
}
function withdraw(uint amount) external {
// 1. CHECKS
require(balances[msg.sender] >= amount, "Insufficient");
// 2. EFFECTS (state changes)
balances[msg.sender] -= amount;
// 3. INTERACTIONS (external calls)
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
## [CRITICAL] ETH-001: Reentrancy in withdraw()
**Location**: `contracts/Vault.sol:45`
**Confidence**: 0.95
### Evidence
State update at line 48 occurs AFTER external call at line 46:
```solidity
function withdraw(uint amount) external {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}(""); // line 46
require(success);
balances[msg.sender] -= amount; // line 48 — AFTER call!
}
function withdraw(uint amount) external nonReentrant {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // Update BEFORE call
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
## 6. Quick Checklist
- [ ] ALL external calls (.call, .transfer, .send) have state updated BEFORE
- [ ] nonReentrant modifier on ALL state-changing functions with external calls
- [ ] No cross-function paths share mutable state with external calls
- [ ] ERC-777 token interactions use ReentrancyGuard
- [ ] Flash loan callbacks follow CEI pattern
- [ ] View functions not affected by incomplete state during calls
- [ ] TSTORE-based reentrancy locks use namespaced slots (ETH-081)
- [ ] TSTORE lock not bypassable via delegatecall (ETH-083, ETH-084)
- [ ] If using ReentrancyGuardTransient, verify OZ version >= 5.1
tools
Advanced Solidity/EVM smart contract security auditor with 104 vulnerability patterns, multi-tool integration, and professional report generation.
development
Comprehensive Solidity contract security scanner detecting 104 vulnerability patterns across reentrancy, access control, arithmetic, DeFi, proxy, and token categories. Integrates Slither, Aderyn, and Mythril with manual analysis.
testing
Analyzes storage layout, proxy patterns, and state variable security in Solidity contracts. Detects storage collisions, uninitialized pointers, and upgrade risks. Use when auditing proxy/upgradeable contracts.
development
Validates Solidity implementation against specification documents. Extracts behavior from docs (README, specs, NatSpec) and verifies code matches documented intent. Uses Trail of Bits methodology for divergence detection.