GREP ARSENAL — MASTER REFERENCE
All grep commands in one place. Run in the first 30 minutes of any new target. Replaces: 03-grep-surface-map, 14-grep-master-patterns + grep sections from 04-13
HOW TO USE THE SURFACE MAP
Process:
- Run ALL 10 blocks below (takes ~5 min)
- Collect all results in a notes file
- Tier-rank the hits (see Tier System below)
- In pass 1: READ everything, DON'T investigate yet
- In pass 2: Deep-dive on Tier 1 + 2 items
Tier System:
- Tier 1 — Near privileged code, external calls, or state changes with no guards → Investigate first
- Tier 2 — Interesting patterns that need context before judging → Investigate after Tier 1
- Tier 3 — Informational only (documentation, test files, comments) → Skip unless Tier 1+2 exhausted
THE 10 GREP BLOCKS (Copy-Paste Each)
Block 1 — Access Control
bashecho "=== ACCESS CONTROL ===" && \ grep -rn "tx\.origin" src/ --include="*.sol" && \ grep -rn "msg\.sender == owner\b" src/ --include="*.sol" && \ grep -rn "modifier only" src/ --include="*.sol" -A5 && \ grep -rn "onlyOwner\|onlyAdmin\|onlyRole" src/ --include="*.sol" | wc -l && \ grep -rn "def admin_\|router\..*admin\|function.*[Aa]dmin" src/ --include="*.sol"
Red flags:
tx.originused for auth → Tier 1 (phishing vector)- Modifier uses
if (condition) { _; }without else → Tier 1 (silent bypass — function still executes for unauthorized callers) onlyOwnercount << total external function count → likely missing guards on siblings
Block 2 — Reentrancy
bashecho "=== REENTRANCY ===" && \ grep -rn "\.call{value\|\.call(" src/ --include="*.sol" && \ grep -rn "\.transfer(\|\.send(" src/ --include="*.sol" && \ grep -rn "safeTransfer\|safeTransferFrom" src/ --include="*.sol" && \ grep -rn "onERC721Received\|onERC1155Received\|tokensReceived" src/ --include="*.sol" && \ grep -rn "nonReentrant\|ReentrancyGuard" src/ --include="*.sol"
Red flags:
.call{value:}orsafeTransferBEFORE state updates in same function → Tier 1 (CEI violation)onERC721Received/onERC1155Receivedhooks present → check for reentrancy path- External calls present but
nonReentrantmissing → verify CEI is followed
Block 3 — Oracle / Price
bashecho "=== ORACLE / PRICE ===" && \ grep -rn "slot0\b" src/ --include="*.sol" && \ grep -rn "getReserves()" src/ --include="*.sol" && \ grep -rn "latestRoundData\|latestAnswer" src/ --include="*.sol" && \ grep -rn "updatedAt" src/ --include="*.sol" && \ grep -rn "block\.timestamp" src/ --include="*.sol" | grep -v "//\|test\|Test" | head -20
Red flags:
slot0()used for price → Tier 1 (Uniswap V3 spot, flash-loan manipulable)getReserves()used for price → Tier 1 (Uniswap V2 spot, flash-loan manipulable)latestRoundDatawithoutupdatedAtcheck → Tier 1 (stale Chainlink price)latestAnswer→ Tier 1 (deprecated, no round validation)
Block 4 — Arithmetic / Math
bashecho "=== ARITHMETIC ===" && \ grep -rn "unchecked {" src/ --include="*.sol" && \ grep -rn "/ \|/=" src/ --include="*.sol" | grep -v "//\|test\|Test" | head -30 && \ grep -rn "mulDiv\|FullMath\|PRBMath" src/ --include="*.sol" && \ grep -rn "\* 10\*\*\|* 1e18\|* WAD\|* RAY" src/ --include="*.sol"
Red flags:
unchecked {}blocks → manually verify each (Solidity 0.8+ unwraps here)- Division before multiplication (
a / b * c) → precision loss / 1e18in contract that handles 6-decimal tokens → decimal mismatch
Block 5 — Input Validation
bashecho "=== INPUT VALIDATION ===" && \ grep -rn "address(0)\b" src/ --include="*.sol" && \ grep -rn "require.*length\|\.length ==" src/ --include="*.sol" && \ grep -rn "delegatecall" src/ --include="*.sol" && \ grep -rn "abi\.decode\|abi\.encodePacked" src/ --include="*.sol" | head -20
Red flags:
delegatecallwith user-controlled target → Tier 1 (arbitrary code execution)abi.decodeon user-supplied calldata without length validation → Tier 1- Array params in batch functions without dedup check → Tier 1 (double-count attack)
Block 6 — Token Handling
bashecho "=== TOKEN HANDLING ===" && \ grep -rn "IERC20\.\|ERC20\." src/ --include="*.sol" | grep "transfer\b\|transferFrom\b" && \ grep -rn "SafeERC20\|safeTransfer\b" src/ --include="*.sol" | head -10 && \ grep -rn "balanceOf(address(this))" src/ --include="*.sol" && \ grep -rn "permit(" src/ --include="*.sol" | grep -v "//\|IERC20Permit" && \ grep -rn "try.*permit\|catch.*permit" src/ --include="*.sol"
Red flags:
token.transfer()withoutSafeERC20.safeTransfer()→ Tier 1 (return value unchecked, fails silently on old USDT)balanceOf(address(this))for pricing/shares → Tier 1 (donation attack vector)permit()without try/catch wrapper → Tier 2 (frontrun DoS possible)
Block 7 — ERC4626 / Vault
bashecho "=== ERC4626 / VAULT ===" && \ grep -rn "totalAssets\|convertToShares\|previewDeposit\|previewMint" src/ --include="*.sol" && \ grep -rn "_decimalsOffset\|decimalsOffset\|virtual_shares\|dead.*shares" src/ --include="*.sol" && \ grep -rn "shares.*supply\|totalSupply\|mint.*shares" src/ --include="*.sol" | head -20
Red flags:
- ERC4626 present but
_decimalsOffset()NOT present → Tier 1 (first depositor inflation) totalAssets()usesbalanceOf(address(this))→ Tier 1 (donation attack)mint()ordeposit()called without same validation path → Tier 1 (MetaPool bug: mint skipped receipt check)
Block 8 — Proxy / Upgradeable
bashecho "=== PROXY / UPGRADE ===" && \ grep -rn "_authorizeUpgrade\|upgradeTo\|upgradeToAndCall" src/ --include="*.sol" && \ grep -rn "initialize(" src/ --include="*.sol" | grep -v "//\|test\|Test" && \ grep -rn "_disableInitializers\|initializer\b\|Initializable" src/ --include="*.sol" && \ grep -rn "StorageSlot\|ERC1967\|TransparentProxy\|UUPSUpgradeable" src/ --include="*.sol"
Red flags:
_authorizeUpgrade()withoutonlyOwneror role check → Tier 1 (anyone can upgrade)initialize()withoutinitializermodifier → Tier 1 (re-initialization possible)- Proxy present but
_disableInitializers()NOT in impl constructor → Tier 1 (impl attackable)
Block 9 — Signature / Replay
bashecho "=== SIGNATURES ===" && \ grep -rn "ecrecover\|ECDSA\.recover" src/ --include="*.sol" && \ grep -rn "chainId\|block\.chainid\|DOMAIN_SEPARATOR" src/ --include="*.sol" && \ grep -rn "nonces\[" src/ --include="*.sol" && \ grep -rn "keccak256.*abi\.encode" src/ --include="*.sol" | head -20
Red flags:
ecrecoverpresent butchainId/DOMAIN_SEPARATORNOT present → Tier 1 (cross-chain replay)ecrecoverwithout nonce → Tier 1 (same-chain replay)ecrecoverreturn not checked againstaddress(0)→ Tier 1 (invalid sigs succeed)
Block 10 — State Completeness / Access
bashecho "=== STATE COMPLETENESS ===" && \ grep -rn "grantRole\|revokeRole\|hasRole" src/ --include="*.sol" && \ grep -rn "bytes32.*ROLE\s*=" src/ --include="*.sol" && \ grep -rn "function.*migrate\|function.*batch.*stake\|function.*multiStake" src/ --include="*.sol" -A10 && \ grep -rn "} catch" src/ --include="*.sol" -A5 | grep -A5 "revert\|Error" && \ grep -rn "cached\|_cache\|lastKnown\|storedBalance" src/ --include="*.sol"
Red flags:
- Role defined but
grantRole()call for that role NOT found anywhere → Tier 1 (role permanently empty) - Array-based function:
flag = trueOUTSIDE/AFTER loop → Tier 1 (empty array bypass) } catch { revert }on critical path → Tier 2 (liveness DoS if external changes)- Cache variable initialized to 0 with no first-access guard → Tier 1 (uninitialized cache)
PROTOCOL-SPECIFIC PATTERNS
Yield Aggregator (like Ern, Yearn, Beefy)
bashgrep -rn "cumulativeReward\|rewardPerShare\|accRewardPerShare" src/ --include="*.sol" grep -rn "harvestCooldown\|canHarvest\|performHarvest\|_harvest" src/ --include="*.sol" grep -rn "totalDeposited\|totalPrincipal" src/ --include="*.sol" # Check: does cumulativeReward always update before user checkpoint?
Lending Protocol (like Aave, Compound)
bashgrep -rn "collateral\|borrow\|liquidat" src/ --include="*.sol" grep -rn "healthFactor\|isSolvent\|isLiquidatable" src/ --include="*.sol" grep -rn "interest.*accrual\|accrueInterest\|indexIncrease" src/ --include="*.sol" grep -rn "amplification\|A_PARAMETER\|getA()" src/ --include="*.sol" # Check: is price oracle manipulation possible? Is interest accrual order correct?
AMM / DEX
bashgrep -rn "getReserves\|reserve0\|reserve1" src/ --include="*.sol" grep -rn "slot0\|sqrtPriceX96\|tick\b" src/ --include="*.sol" grep -rn "K\s*=\|invariant\|_invariant" src/ --include="*.sol" grep -rn "amountOutMin\|minAmountOut\|deadline" src/ --include="*.sol" # Check: is spot price used for any security decision? Missing slippage?
Staking / Restaking
bashgrep -rn "epoch\|currentEpoch\|lastEpoch\|epochId" src/ --include="*.sol" grep -rn "unstake\|migrate\|slash\|jailValidator" src/ --include="*.sol" grep -rn "validatorIds\|stakeIds\|delegateIds" src/ --include="*.sol" # Check: can same ID be passed twice? Empty array skips state reset?
ZK / Proof Contracts
bashgrep -rn "verifyProof\|IVerifier\|publicInputs\b" src/ --include="*.sol" grep -rn "return true" src/ --include="*.sol" | grep -i "verify\|proof" grep -rn "require.*inputs\[0\]\|rangeCheck\|MAX_BALANCE" src/ --include="*.sol" # Check: are public inputs range-checked after proof verification?
2025 NEW PATTERN SCANS
bash# ERC4626 near-empty vault (inflation variant, now widespread) grep -rn "totalAssets\b" src/ --include="*.sol" -A10 | grep "balanceOf\|balance()" # If totalAssets() uses raw balanceOf → donation attack risk # EIP-2612 permit frontrun DoS grep -rn "permitAndDeposit\|permitAndStake\|permitAndBorrow" src/ --include="*.sol" grep -rn "try.*permit\|catch.*permit" src/ --include="*.sol" # Missing try/catch around permit = DoS possible # Decimal precision mismatch (6 vs 18 decimals) grep -rn "/ 1e18\|/ WAD" src/ --include="*.sol" grep -rn "decimals()\|IERC20Metadata" src/ --include="*.sol" # / 1e18 WITHOUT decimals() call in same function = decimal mismatch candidate # Uninitialized cache grep -rn "uint256.*cached\|uint128.*cached\|int256.*cached" src/ --include="*.sol" # Cache initialized to 0 with no first-access sync = uninitialized cache bug # Empty array bypass grep -rn "= true;" src/ --include="*.sol" -B15 | grep -B15 "for.*calldata" # flag = true AFTER a loop that can be empty = bypass critical # Streaming/continuous precision (per-second rewards) grep -rn "rewardPerSecond\|ratePerSecond\|flowRate\|perSecond" src/ --include="*.sol" grep -rn "uint128.*reward\|uint96.*rate" src/ --include="*.sol" # uint128 accumulator × per-second rate = overflow risk on long time periods # Withdrawal queue multi-field invariant grep -rn "\.queued\b\|\.claimable\b\|\.claimed\b" src/ --include="*.sol" # claimed <= claimable <= queued must hold — check all update paths # Cross-chain signature reuse grep -rn "keccak256.*abi\.encodePacked\|ECDSA\.recover" src/ --include="*.sol" grep -rn "chainId\|block\.chainid" src/ --include="*.sol" # ecrecover WITHOUT chainId = same sig valid on all chains # try/catch DoS liveness grep -rn "} catch" src/ --include="*.sol" -A5 | grep -B1 "revert\|IncompatibleAdapter" # Nullifier timing (Worldcoin-style) grep -rn "nullifiers\[.*\] = true\|nullifierUsed\[" src/ --include="*.sol" -B5 # Marked before action = frontrun can permanently DoS identity
SPECIFIC BUG PATTERN SEARCHES
Silent Modifier (if vs require)
bash# Find modifiers that use if() without revert — silently does nothing for unauthorized callers grep -rn "modifier only" src/ --include="*.sol" -A10 | grep -A10 "if (" # Correct: require(condition, "msg"); _; # BUG: if (condition) { _; } ← no else = still executes for unauthorized
Tautology Check (variable compared to itself)
bashgrep -rn "require(" src/ --include="*.sol" | grep "\b\(\w\+\) == \1\b" # e.g. require(a == a, ...) = always true → always passes
Same-Role Count Mismatch
bashgrep -rn "onlyRole\b" src/ --include="*.sol" | wc -l grep -rn "grantRole(" src/ --include="*.sol" | wc -l # More onlyRole uses than grantRole calls = some roles may never be granted
Accounting: Balance vs Tracked
bashgrep -rn "balanceOf(address(this))" src/ --include="*.sol" grep -rn "totalDeposited\|totalPrincipal\|_balance\b" src/ --include="*.sol" # If protocol uses raw balanceOf for pricing AND has separate totalDeposited # → donation attack: send tokens directly to contract → inflates balance
→ NEXT: 04-poc-and-foundry.md

