Solidity Code Review & Security Guide
When to Apply
- Writing or implementing contracts and applying secure-by-default patterns.
- Performing a security audit, peer review, or general assessment of Solidity code.
- Auditing access control, external-call safety, and upgradeability logic.
- Preparing for a security audit or bug bounty, or responding to an incident.
- Debugging unexpected behavior in external contract interactions.
Security Thinking Framework
Apply these foundational principles as a mental checklist. Each addresses a category of vulnerability and guides your reasoning.
Core Principles
| Principle |
What It Means |
What to Verify |
| Checks-Effects-Interactions (CEI) |
Validate inputs, update state, then interact externally |
State changes complete before any external call |
| Least Privilege |
Every function and role has the minimum access required |
Sensitive functions have appropriate access modifiers |
| Defense in Depth |
Multiple layers of protection, no single points of failure |
Combine CEI + ReentrancyGuard + SafeERC20 where applicable |
| Fail-Safe Defaults |
The default state is secure; access must be explicitly granted |
Functions default to restricted, not open |
| Complete Mediation |
Every access to every resource is validated |
No code paths bypass access control checks |
Security Decision Process
For each function, ask in order:
- Who can call this? — Access control (onlyOwner, hasRole, msg.sender validation)
- What inputs does it accept? — Validate parameters (zero address, bounds, empty values)
- What state does it change? — State updates happen before external interactions
- Does it interact externally? — Apply CEI, use SafeERC20, check return values
- Can it be called recursively? — Add ReentrancyGuard if external calls are present
- Is the state change visible? — Emit events for off-chain tracking
- Can it be paused? — Circuit breakers for critical operations
Pre-Review Checklist
- Compilation: Code compiles without errors using the project's build system (Foundry, Hardhat).
- Test Suite: Existing tests pass; review coverage to find untested logic.
- Dependencies: External libraries and inherited contracts use pinned, trusted versions.
- Documentation: Read specs and NatSpec to understand intended behavior.
- Known Issues: Check previous audits and documented "known risks".
- Scope: Define the exact contracts and functions in scope.
Review Methodology
- Scope & Architecture: Map inheritance, external dependencies, and system architecture.
- Manual Line-by-Line Review: Deep-dive critical functions, focusing on state changes and value transfers.
- Automated Analysis: Run static analysis (Slither, Aderyn, Solhint) for common patterns and style.
- Vulnerability Pattern Matching: Check known SCWE patterns (reentrancy, access control, etc.).
- Integration & Edge Cases: Analyze contract interactions and boundary conditions (zero values, max integers).
Severity Classification
| Severity |
Criteria |
Examples |
| Critical |
Direct loss of funds, permanent contract lock, or total compromise. |
Reentrancy, Unprotected withdraw, Logic error in transfer. |
| High |
Significant impact on system functionality or exploitable under realistic conditions. |
Access control bypass, Unchecked external calls, Oracle manipulation. |
| Medium |
Limited impact or requires specific, difficult-to-achieve conditions. |
Timestamp dependence, Front-running, Denial of Service (DoS). |
| Low |
Best practice violations, informational findings, or minor optimizations. |
Missing events, Floating pragma, Unused variables. |
Key Inspection Areas
Organized by OWASP SCSVS threat model. For each area, verify the listed controls.
Access Control & Authorization (SCSVS-AUTH)
onlyOwner or role-based access on ALL state-changing functions.
- Initializers protected and callable once only.
- No
tx.origin for authentication — use msg.sender.
- Privileged roles guarded by multi-sig or timelock.
External Calls & Reentrancy (SCSVS-COMM)
- Follow Checks-Effects-Interactions strictly; state updates before external calls.
ReentrancyGuard on functions making external calls; watch cross-function/cross-contract reentrancy.
- Use
call() (not transfer/send) for ETH; handle and check all return values.
- Pull-over-push pattern for payments.
Arithmetic & Type Safety (SCSVS-CODE)
- For Solidity <0.8.0, ensure
SafeMath. Check precision loss (multiply before dividing).
- Verify safe casting between types (
uint256 → uint8).
- Explicit visibility, fixed pragma, no deprecated constructs, no shadowing.
Token Handling (ERC20/721)
- Use
SafeERC20 for transfer/transferFrom; account for fee-on-transfer tokens.
- Handle
approve race conditions; verify onERC721Received reentrancy.
Upgrade Safety
- Storage gaps in logic contracts to prevent collisions; verify storage compatibility across upgrades.
- Logic contracts avoid
selfdestruct/untrusted delegatecall; proxy admin access restricted.
Cryptography & Randomness (SCSVS-CRYPTO)
- No on-chain randomness (
block.timestamp, blockhash) — use Chainlink VRF or commit-reveal.
- EIP-712 for structured signatures; nonces to prevent replay; validate
ecrecover != address(0).
abi.encode over abi.encodePacked for dynamic types in hashing.
DeFi & Oracle Safety (SCSVS-DEFI)
- Slippage protection on swaps/liquidity; flash-loan resistance in price-sensitive logic.
- Oracle manipulation protection (TWAP, multiple sources); no reliance on
address(this).balance for accounting.
Events & NatSpec (SCWE-063)
Events are the audit trail. Reason about them from the transaction logic, not just syntax — for every state transition an auditor or off-chain indexer would need to follow, verify an event exists, fires, and logs the truth. When one is missing or wrong, propose the correct declaration and emit.
- Missing emission: every state-changing public/external function emits an event (token transfers, ownership/role changes, config updates, upgrades, fund movements). A silent state change is an audit gap — propose the event.
- Declared but never emitted: an
event that is declared yet never emitted is a forgotten emission or dead code. Either wire it to the relevant state change or remove it.
- Incorrect data: the logged values must reflect the actual post-conditions (e.g. log the amount actually transferred, after a successful call — not the requested amount). Emit only after the effect succeeds.
- Missing
indexed: mark key fields (addresses, ids) as indexed so off-chain consumers can filter logs efficiently (max 3 indexed topics per event).
- No sensitive data: never log secrets, raw auth hashes, or confidential business logic.
- NatSpec: accurate
@notice, @param, @return; @dev for complex logic and security assumptions.
Style Guide Compliance
- Naming: PascalCase (contracts), camelCase (functions), UPPER_CASE (constants).
- Function ordering by visibility (external → public → internal → private), then mutability.
- Modifier order: visibility, mutability, virtual, override, custom.
- See the Solidity Style Guide Reference.
Secure Patterns by Priority
Critical — CEI pattern (state before external calls); ReentrancyGuard (mutex on external interactions); access control on every state-changing function; SafeERC20 + checked .call() returns.
High — Input validation (zero address, bounds, empty arrays) with gas-efficient custom errors; upgrade safety (initializer modifier, storage compatibility); circuit breakers (Pausable) for fund-handling protocols.
Medium — Signature security (nonces + EIP-712); no on-chain randomness; events on all significant state changes.
Code Improvement Proposals
A review is not complete at "is it safe?" — also surface how the code could be better. For each reviewed unit, consider proposing:
- Better patterns: Replace ad-hoc access checks with
AccessControl; replace manual reentrancy flags with ReentrancyGuard; replace require(string) with custom errors.
- Simpler design: Reduce state surface, remove redundant storage reads, collapse duplicated logic into a single internal function.
- Gas efficiency: Pack storage, prefer
calldata, cache array length, evaluate Solady alternatives — only when it does not compromise clarity or safety.
- Readability & maintainability: Clearer naming, NatSpec on non-obvious invariants, splitting oversized functions.
- Testability: Pure logic separated from I/O, events that make off-chain assertions possible.
Frame each proposal as: current approach → suggested approach → concrete benefit (safety / gas / clarity). Mark proposals as Low/informational unless they fix a real defect.
Reporting Format
[SEVERITY] Finding Title
ID: SCWE-XXX (replace with actual SCWE ID, e.g., SCWE-046 — see search_vulnerabilities)
Location: ContractName.sol:L42
Description: Detailed explanation of the vulnerability and how it can be triggered.
Impact: What happens if exploited (e.g., "User funds can be stolen").
Remediation: Specific code changes or architectural adjustments to fix the issue.
Enhanced with MCP
When using the solidity-agent-toolkit, run a structured review workflow:
Static Analysis — run_slither (SCWE-mapped findings), run_aderyn (fast Rust scanner), run_solhint (lint/style).
Pattern Detection — match_vulnerability_patterns (regex detection of 32+ patterns).
Vulnerability Lookup — search_vulnerabilities (SCWE database by keyword), get_remediation (fix guidance with code examples), check_vulnerability (match code to a known SCWE pattern).
Style & Quality — check_style (12 rules), format_code, validate_natspec.
Reference Data — scwe://{id}, scwe://category/{category}, sctop10://list.
Guided Prompts — security_audit (structured audit), code_review (quality assessment), vulnerability_fix (step-by-step remediation).
For attacker-perspective threat modeling, see the Adversarial Analysis skill.
References
1---2name: solidity-code-review3description: Smart contract code review, security best practices, and audit methodology for Solidity. Use when writing, implementing, reviewing, auditing, or assessing the security of Solidity code. Covers the security thinking framework (CEI, least privilege, defense in depth), structured review process, severity classification, key inspection areas, secure patterns (reentrancy prevention, access control, SafeERC20, upgrade safety), OWASP SCWE Top 10, code improvement proposals, and reporting. Triggers on tasks involving code review, security audit, vulnerability detection, vulnerability assessment, access control, CEI pattern, ReentrancyGuard, SafeERC20, best practices check, or smart contract review.4license: MIT5---67# Solidity Code Review & Security Guide89## When to Apply1011- Writing or implementing contracts and applying secure-by-default patterns.12- Performing a security audit, peer review, or general assessment of Solidity code.13- Auditing access control, external-call safety, and upgradeability logic.14- Preparing for a security audit or bug bounty, or responding to an incident.15- Debugging unexpected behavior in external contract interactions.1617## Security Thinking Framework1819Apply these foundational principles as a mental checklist. Each addresses a category of vulnerability and guides your reasoning.2021### Core Principles2223| Principle | What It Means | What to Verify |24| :------------------------------------ | :------------------------------------------------------------- | :--------------------------------------------------------- |25| **Checks-Effects-Interactions (CEI)** | Validate inputs, update state, then interact externally | State changes complete before any external call |26| **Least Privilege** | Every function and role has the minimum access required | Sensitive functions have appropriate access modifiers |27| **Defense in Depth** | Multiple layers of protection, no single points of failure | Combine CEI + ReentrancyGuard + SafeERC20 where applicable |28| **Fail-Safe Defaults** | The default state is secure; access must be explicitly granted | Functions default to restricted, not open |29| **Complete Mediation** | Every access to every resource is validated | No code paths bypass access control checks |3031### Security Decision Process3233For each function, ask in order:34351. **Who can call this?** — Access control (onlyOwner, hasRole, msg.sender validation)362. **What inputs does it accept?** — Validate parameters (zero address, bounds, empty values)373. **What state does it change?** — State updates happen before external interactions384. **Does it interact externally?** — Apply CEI, use SafeERC20, check return values395. **Can it be called recursively?** — Add ReentrancyGuard if external calls are present406. **Is the state change visible?** — Emit events for off-chain tracking417. **Can it be paused?** — Circuit breakers for critical operations4243## Pre-Review Checklist4445- **Compilation**: Code compiles without errors using the project's build system (Foundry, Hardhat).46- **Test Suite**: Existing tests pass; review coverage to find untested logic.47- **Dependencies**: External libraries and inherited contracts use pinned, trusted versions.48- **Documentation**: Read specs and NatSpec to understand intended behavior.49- **Known Issues**: Check previous audits and documented "known risks".50- **Scope**: Define the exact contracts and functions in scope.5152## Review Methodology53541. **Scope & Architecture**: Map inheritance, external dependencies, and system architecture.552. **Manual Line-by-Line Review**: Deep-dive critical functions, focusing on state changes and value transfers.563. **Automated Analysis**: Run static analysis (Slither, Aderyn, Solhint) for common patterns and style.574. **Vulnerability Pattern Matching**: Check known SCWE patterns (reentrancy, access control, etc.).585. **Integration & Edge Cases**: Analyze contract interactions and boundary conditions (zero values, max integers).5960## Severity Classification6162| Severity | Criteria | Examples |63| :----------- | :------------------------------------------------------------------------------------ | :-------------------------------------------------------------------- |64| **Critical** | Direct loss of funds, permanent contract lock, or total compromise. | Reentrancy, Unprotected `withdraw`, Logic error in `transfer`. |65| **High** | Significant impact on system functionality or exploitable under realistic conditions. | Access control bypass, Unchecked external calls, Oracle manipulation. |66| **Medium** | Limited impact or requires specific, difficult-to-achieve conditions. | Timestamp dependence, Front-running, Denial of Service (DoS). |67| **Low** | Best practice violations, informational findings, or minor optimizations. | Missing events, Floating pragma, Unused variables. |6869## Key Inspection Areas7071Organized by OWASP SCSVS threat model. For each area, verify the listed controls.7273### Access Control & Authorization (SCSVS-AUTH)7475- `onlyOwner` or role-based access on ALL state-changing functions.76- Initializers protected and callable once only.77- No `tx.origin` for authentication — use `msg.sender`.78- Privileged roles guarded by multi-sig or timelock.7980### External Calls & Reentrancy (SCSVS-COMM)8182- Follow Checks-Effects-Interactions strictly; state updates before external calls.83- `ReentrancyGuard` on functions making external calls; watch cross-function/cross-contract reentrancy.84- Use `call()` (not `transfer`/`send`) for ETH; handle and check all return values.85- Pull-over-push pattern for payments.8687### Arithmetic & Type Safety (SCSVS-CODE)8889- For Solidity <0.8.0, ensure `SafeMath`. Check precision loss (multiply before dividing).90- Verify safe casting between types (`uint256` → `uint8`).91- Explicit visibility, fixed pragma, no deprecated constructs, no shadowing.9293### Token Handling (ERC20/721)9495- Use `SafeERC20` for `transfer`/`transferFrom`; account for fee-on-transfer tokens.96- Handle `approve` race conditions; verify `onERC721Received` reentrancy.9798### Upgrade Safety99100- Storage gaps in logic contracts to prevent collisions; verify storage compatibility across upgrades.101- Logic contracts avoid `selfdestruct`/untrusted `delegatecall`; proxy admin access restricted.102103### Cryptography & Randomness (SCSVS-CRYPTO)104105- No on-chain randomness (`block.timestamp`, `blockhash`) — use Chainlink VRF or commit-reveal.106- EIP-712 for structured signatures; nonces to prevent replay; validate `ecrecover != address(0)`.107- `abi.encode` over `abi.encodePacked` for dynamic types in hashing.108109### DeFi & Oracle Safety (SCSVS-DEFI)110111- Slippage protection on swaps/liquidity; flash-loan resistance in price-sensitive logic.112- Oracle manipulation protection (TWAP, multiple sources); no reliance on `address(this).balance` for accounting.113114### Events & NatSpec (SCWE-063)115116Events are the audit trail. Reason about them from the transaction logic, not just syntax — for every state transition an auditor or off-chain indexer would need to follow, verify an event exists, fires, and logs the truth. When one is missing or wrong, propose the correct declaration and `emit`.117118- **Missing emission**: every state-changing public/external function emits an event (token transfers, ownership/role changes, config updates, upgrades, fund movements). A silent state change is an audit gap — propose the event.119- **Declared but never emitted**: an `event` that is declared yet never `emit`ted is a forgotten emission or dead code. Either wire it to the relevant state change or remove it.120- **Incorrect data**: the logged values must reflect the actual post-conditions (e.g. log the amount actually transferred, after a successful call — not the requested amount). Emit only after the effect succeeds.121- **Missing `indexed`**: mark key fields (addresses, ids) as `indexed` so off-chain consumers can filter logs efficiently (max 3 indexed topics per event).122- **No sensitive data**: never log secrets, raw auth hashes, or confidential business logic.123- NatSpec: accurate `@notice`, `@param`, `@return`; `@dev` for complex logic and security assumptions.124125### Style Guide Compliance126127- Naming: PascalCase (contracts), camelCase (functions), UPPER_CASE (constants).128- Function ordering by visibility (external → public → internal → private), then mutability.129- Modifier order: visibility, mutability, virtual, override, custom.130- See the [Solidity Style Guide Reference](./references/solidity-style-guide.md).131132## Secure Patterns by Priority133134**Critical** — CEI pattern (state before external calls); ReentrancyGuard (mutex on external interactions); access control on every state-changing function; SafeERC20 + checked `.call()` returns.135136**High** — Input validation (zero address, bounds, empty arrays) with gas-efficient custom errors; upgrade safety (`initializer` modifier, storage compatibility); circuit breakers (Pausable) for fund-handling protocols.137138**Medium** — Signature security (nonces + EIP-712); no on-chain randomness; events on all significant state changes.139140## Code Improvement Proposals141142A review is not complete at "is it safe?" — also surface how the code could be **better**. For each reviewed unit, consider proposing:143144- **Better patterns**: Replace ad-hoc access checks with `AccessControl`; replace manual reentrancy flags with `ReentrancyGuard`; replace `require(string)` with custom errors.145- **Simpler design**: Reduce state surface, remove redundant storage reads, collapse duplicated logic into a single internal function.146- **Gas efficiency**: Pack storage, prefer `calldata`, cache array length, evaluate Solady alternatives — only when it does not compromise clarity or safety.147- **Readability & maintainability**: Clearer naming, NatSpec on non-obvious invariants, splitting oversized functions.148- **Testability**: Pure logic separated from I/O, events that make off-chain assertions possible.149150Frame each proposal as: **current approach → suggested approach → concrete benefit (safety / gas / clarity)**. Mark proposals as `Low`/informational unless they fix a real defect.151152## Reporting Format153154### [SEVERITY] Finding Title155156**ID**: SCWE-XXX _(replace with actual SCWE ID, e.g., SCWE-046 — see `search_vulnerabilities`)_157**Location**: `ContractName.sol:L42`158**Description**: Detailed explanation of the vulnerability and how it can be triggered.159**Impact**: What happens if exploited (e.g., "User funds can be stolen").160**Remediation**: Specific code changes or architectural adjustments to fix the issue.161162## Enhanced with MCP163164When using the `solidity-agent-toolkit`, run a structured review workflow:165166**Static Analysis** — `run_slither` (SCWE-mapped findings), `run_aderyn` (fast Rust scanner), `run_solhint` (lint/style).167168**Pattern Detection** — `match_vulnerability_patterns` (regex detection of 32+ patterns).169170**Vulnerability Lookup** — `search_vulnerabilities` (SCWE database by keyword), `get_remediation` (fix guidance with code examples), `check_vulnerability` (match code to a known SCWE pattern).171172**Style & Quality** — `check_style` (12 rules), `format_code`, `validate_natspec`.173174**Reference Data** — `scwe://{id}`, `scwe://category/{category}`, `sctop10://list`.175176**Guided Prompts** — `security_audit` (structured audit), `code_review` (quality assessment), `vulnerability_fix` (step-by-step remediation).177178For attacker-perspective threat modeling, see the **Adversarial Analysis** skill.179180## References181182- [OWASP Smart Contract Top 10 (2026)](./references/owasp-scwe-top10.md)183- [Smart Contract Audit Checklist](./references/audit-checklist.md)184- [Solidity Style Guide Reference](./references/solidity-style-guide.md)