---
# === IDENTITY ===
id: software/security/smart-contract-audit/2026
canonical_question: "What is the smart contract security audit checklist (Solidity)?"
aliases:
  - "Solidity security audit checklist"
  - "smart contract vulnerability checklist"
  - "how to audit a Solidity smart contract"
  - "Ethereum smart contract security best practices"
  - "DeFi smart contract audit guide"
  - "Solidity reentrancy prevention"
  - "smart contract static analysis tools"
  - "SWC registry vulnerability list"
entity_type: software_reference
domain: software > security > smart contract audit
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-24
confidence: 0.88
version: 1.0
first_published: 2026-02-24

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Solidity 0.8.0 (Dec 2020) — built-in overflow/underflow checks; EEA EthTrust Security Levels v2 (Dec 2023)"
  next_review: 2026-08-23
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Automated tools catch only ~30-50% of vulnerabilities — always combine with manual expert review"
  - "Solidity 0.8.x+ has built-in overflow protection; for 0.7.x and below, SafeMath is mandatory"
  - "Never deploy unaudited contracts that handle user funds — immutability means bugs cannot be patched post-deployment"
  - "Audit the deployed bytecode, not just the source — compiler optimizations can introduce unexpected behavior"
  - "Re-audit after every significant code change — even single-line changes can introduce critical vulnerabilities"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "You are auditing a Move or Rust/Solana smart contract, not Solidity/EVM"
    use_instead: "Solana/Move-specific audit guides — different VM, different vulnerability classes"
  - condition: "You need a general web application security checklist, not blockchain-specific"
    use_instead: "software/security/owasp-top-10/2026"
  - condition: "You only need gas optimization, not security"
    use_instead: "Solidity gas optimization guides — different concern than security"

# === AGENT HINTS ===
inputs_needed:
  - key: "solidity_version"
    question: "What Solidity compiler version is the contract using?"
    type: choice
    options: ["0.8.x+ (current)", "0.7.x or below (legacy)", "unsure"]
  - key: "contract_type"
    question: "What type of contract are you auditing?"
    type: choice
    options: ["DeFi (lending, DEX, yield)", "Token (ERC-20, ERC-721, ERC-1155)", "DAO/Governance", "Bridge/Cross-chain", "Upgradeable proxy", "Other"]
  - key: "audit_scope"
    question: "What level of audit are you performing?"
    type: choice
    options: ["Automated scanning only", "Manual review + automated tools", "Full audit (manual + static + fuzzing + formal verification)"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/security/smart-contract-audit/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-24)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/security/owasp-top-10/2026"
      label: "OWASP Top 10"
    - id: "software/patterns/error-handling-strategies/2026"
      label: "Error Handling Strategies"
  solves: []
  alternative_to: []
  often_confused_with: []

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Smart Contract Weakness Classification (SWC) Registry"
    author: SmartContractSecurity
    url: https://swcregistry.io/
    type: community_resource
    published: 2020-01-01
    reliability: high
  - id: src2
    title: "Ethereum Smart Contract Security Best Practices"
    author: ConsenSys Diligence
    url: https://consensys.github.io/smart-contract-best-practices/
    type: technical_blog
    published: 2023-06-15
    reliability: high
  - id: src3
    title: "OpenZeppelin Contracts — Security Utilities"
    author: OpenZeppelin
    url: https://docs.openzeppelin.com/contracts/4.x/api/security
    type: official_docs
    published: 2024-01-10
    reliability: authoritative
  - id: src4
    title: "Slither — Static Analysis Framework for Solidity"
    author: Trail of Bits
    url: https://github.com/crytic/slither
    type: official_docs
    published: 2024-09-01
    reliability: high
  - id: src5
    title: "Smart Contract Security — Ethereum.org"
    author: Ethereum Foundation
    url: https://ethereum.org/developers/docs/smart-contracts/security/
    type: official_docs
    published: 2024-11-01
    reliability: authoritative
  - id: src6
    title: "Security Considerations — Solidity Documentation"
    author: Solidity Team
    url: https://docs.soliditylang.org/en/latest/security-considerations.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src7
    title: "Mythril — Security Analysis Tool for EVM Bytecode"
    author: ConsenSys
    url: https://github.com/ConsenSys/mythril
    type: official_docs
    published: 2024-06-01
    reliability: high
---

# Smart Contract Security Audit Checklist (Solidity)

## TL;DR

- **Bottom line**: Reentrancy, access control flaws, and integer overflow/underflow are the top 3 smart contract vulnerabilities; a comprehensive audit combines manual review, static analysis (Slither/Mythril), fuzzing (Echidna), and formal verification.
- **Key tool/command**: `slither . --detect reentrancy-eth,reentrancy-no-eth,unprotected-upgrade,arbitrary-send-eth`
- **Watch out for**: Reentrancy in cross-contract calls — the #1 cause of DeFi exploits, responsible for billions in losses (The DAO, Curve, Euler).
- **Works with**: Solidity 0.4.x-0.8.x on any EVM-compatible chain (Ethereum, Polygon, Arbitrum, BSC, Optimism).

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Automated tools catch only ~30-50% of vulnerabilities — always combine with manual expert review
- Solidity 0.8.x+ has built-in overflow protection via checked arithmetic; for 0.7.x and below, SafeMath is mandatory
- Never deploy unaudited contracts that handle user funds — immutability means bugs cannot be patched post-deployment without proxy patterns
- Audit the deployed bytecode, not just source — compiler optimizations and versions can introduce unexpected behavior
- Re-audit after every significant code change — even single-line changes can introduce critical vulnerabilities
- Gas optimizations can introduce security issues — never sacrifice safety for gas savings

## Quick Reference

| # | Vulnerability | Severity | SWC ID | Detection | Prevention |
|---|---|---|---|---|---|
| 1 | Reentrancy | Critical | SWC-107 | Slither `reentrancy-eth`, Mythril | Checks-Effects-Interactions + ReentrancyGuard |
| 2 | Integer Overflow/Underflow | High | SWC-101 | Mythril symbolic execution | Solidity 0.8.x+ (built-in) or SafeMath for <0.8 |
| 3 | Access Control (Unprotected Functions) | Critical | SWC-105 | Slither `unprotected-upgrade` | OpenZeppelin Ownable/AccessControl + explicit modifiers |
| 4 | Front-Running (Transaction Order Dependence) | High | SWC-114 | Manual review | Commit-reveal schemes, batch auctions, or Flashbots Protect |
| 5 | Oracle Manipulation | Critical | N/A | Manual review, historical analysis | TWAP oracles, Chainlink price feeds, multi-oracle design |
| 6 | Unchecked Return Values | High | SWC-104 | Slither `unchecked-lowlevel` | Always check return of `.call()`, `.send()`, `.transfer()` |
| 7 | Delegatecall to Untrusted Callee | Critical | SWC-112 | Slither `delegatecall-loop` | Never delegatecall to user-supplied addresses |
| 8 | tx.origin Authentication | High | SWC-115 | Slither `tx-origin` | Use `msg.sender` instead of `tx.origin` for auth |
| 9 | Flash Loan Attacks | Critical | N/A | Manual review, invariant testing | Same-block price checks, delay mechanisms, access control on sensitive operations |
| 10 | Storage Collision (Proxy Patterns) | Critical | N/A | Slither `uninitialized-storage` | EIP-1967 storage slots, EIP-7201 namespaced storage |
| 11 | DoS with Failed Call | Medium | SWC-113 | Slither `calls-loop` | Pull-over-push payment pattern |
| 12 | Timestamp Dependence | Low | SWC-116 | Mythril | Avoid using `block.timestamp` for critical logic; 15-second tolerance |

## Decision Tree

```
START
|-- Is the contract upgradeable (proxy pattern)?
|   |-- YES --> Check for storage collision (EIP-1967/7201), uninitialized proxy,
|   |           selfdestruct in implementation, and delegatecall safety. See #7, #10.
|   +-- NO  |
|-- Does the contract handle ETH or token transfers?
|   |-- YES --> Check for reentrancy (#1), unchecked returns (#6), flash loan vectors (#9).
|   |           Apply Checks-Effects-Interactions + ReentrancyGuard.
|   +-- NO  |
|-- Does the contract use external price data?
|   |-- YES --> Check for oracle manipulation (#5) and front-running (#4).
|   |           Verify TWAP window, Chainlink heartbeat, multi-oracle fallback.
|   +-- NO  |
|-- Does the contract have admin/owner functions?
|   |-- YES --> Check for access control (#3), centralization risks, privilege escalation.
|   |           Verify role separation, timelocks, multi-sig requirements.
|   +-- NO  |
+-- DEFAULT --> Run full Slither + Mythril scan, review all 12 vulnerability classes above.
```

## Step-by-Step Guide

### 1. Set up the audit environment

Clone the repository, install dependencies, and verify the project compiles cleanly. [src4]

```bash
# Clone and set up
git clone <target-repo>
cd <target-repo>

# Install dependencies (Hardhat or Foundry)
npm install          # for Hardhat projects
# or
forge install        # for Foundry projects

# Compile to verify no errors
npx hardhat compile  # Hardhat
# or
forge build          # Foundry
```

**Verify**: Compilation succeeds with zero errors and zero warnings.

### 2. Run static analysis with Slither

Slither detects 90+ vulnerability patterns in seconds. Run it first to catch low-hanging fruit. [src4]

```bash
# Install Slither
pip install slither-analyzer

# Run full analysis
slither . --json slither-report.json

# Run targeted high-severity detectors
slither . --detect reentrancy-eth,reentrancy-no-eth,arbitrary-send-eth,\
unprotected-upgrade,suicidal,controlled-delegatecall,tx-origin

# Generate inheritance graph (useful for understanding complex contracts)
slither . --print inheritance-graph
```

**Verify**: `cat slither-report.json | python -m json.tool | grep '"impact": "High"'` -- review all High and Medium findings.

### 3. Run symbolic execution with Mythril

Mythril explores execution paths to find vulnerabilities Slither misses (e.g., complex integer issues, path-dependent bugs). [src7]

```bash
# Install Mythril
pip install mythril

# Analyze a single contract
myth analyze contracts/Vault.sol --solv 0.8.20

# Deep analysis (more execution depth, slower)
myth analyze contracts/Vault.sol --execution-timeout 300 --max-depth 50

# Analyze deployed bytecode (on-chain contract)
myth analyze --address 0x<contract_address> --rpc infura
```

**Verify**: Review the Mythril HTML report for any issues marked "SWC-101", "SWC-107", or "SWC-104".

### 4. Fuzz with Echidna or Foundry

Property-based fuzzing discovers edge cases that static analysis cannot. Define invariants and let the fuzzer try to break them. [src5]

```bash
# Foundry fuzzing (built-in)
forge test --fuzz-runs 10000

# Echidna (Trail of Bits property-based fuzzer)
echidna . --contract TestVault --config echidna.yaml
```

```solidity
// Example: Foundry fuzz test for a vault contract
function testFuzz_depositWithdrawInvariant(uint256 amount) public {
    vm.assume(amount > 0 && amount < type(uint128).max);
    token.mint(address(this), amount);
    token.approve(address(vault), amount);
    vault.deposit(amount);
    vault.withdraw(amount);
    // Invariant: balance should return to zero
    assertEq(vault.balanceOf(address(this)), 0);
}
```

**Verify**: `forge test -vvv` -- all fuzz tests pass with 10,000+ runs.

### 5. Manual code review

Systematic line-by-line review focusing on business logic, which tools cannot fully assess. [src2]

Checklist for manual review:
- [ ] All external/public functions have appropriate access control
- [ ] State changes happen before external calls (Checks-Effects-Interactions)
- [ ] No unbounded loops that could hit gas limits
- [ ] All arithmetic in unchecked blocks is intentionally unchecked and safe
- [ ] Proxy storage layout matches implementation storage layout
- [ ] Events emitted for all state-changing operations
- [ ] No hardcoded addresses that should be configurable
- [ ] Token approvals are handled safely (approve-to-zero pattern for USDT)

**Verify**: Document each finding with severity, location, and recommended fix.

### 6. Write the audit report

Consolidate all findings into a structured report with severity classifications. [src5]

```
Report Structure:
1. Executive Summary
2. Scope & Methodology
3. Findings (Critical > High > Medium > Low > Informational)
   - Each finding: Title, Severity, Location, Description, Impact, Recommendation, Status
4. Gas Optimizations
5. Appendix: Tool outputs, test results
```

**Verify**: Every Critical and High finding has a recommended fix and has been discussed with the development team.

## Code Examples

### Solidity: Vulnerable vs Secure Withdrawal (Reentrancy)

> Full script: [solidity-vulnerable-vs-secure-withdrawal-reentranc.txt](scripts/solidity-vulnerable-vs-secure-withdrawal-reentranc.txt) (29 lines)

```solidity
// ============================================
// VULNERABLE: State update AFTER external call
// ============================================
// SWC-107: Reentrancy vulnerability
contract VulnerableVault {
# ... (see full script)
```

### Solidity: Vulnerable vs Secure Access Control

```solidity
// ============================================
// VULNERABLE: No access control on critical function
// ============================================
// SWC-105: Anyone can drain the contract
contract VulnerableToken {
    function mint(address to, uint256 amount) external {
        // BAD: no access restriction — anyone can mint
        _mint(to, amount);
    }
}

// ============================================
// SECURE: Role-based access with OpenZeppelin
// ============================================
import "@openzeppelin/contracts/access/AccessControl.sol";

contract SecureToken is AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }
}
```

### Solidity: tx.origin vs msg.sender

> Full script: [solidity-tx-origin-vs-msg-sender.txt](scripts/solidity-tx-origin-vs-msg-sender.txt) (25 lines)

```solidity
// ============================================
// VULNERABLE: tx.origin for authorization
// ============================================
// SWC-115: Phishing attack via intermediate contract
contract VulnerableWallet {
# ... (see full script)
```

## Anti-Patterns

### Wrong: Unchecked low-level call return value

```solidity
// BAD -- SWC-104: ignoring return value of .call()
function sendEth(address to, uint256 amount) external {
    to.call{value: amount}("");
    // If the call fails silently, ETH is lost
    // and contract state is inconsistent
}
```

### Correct: Always check call return value

```solidity
// GOOD -- check return and revert on failure
function sendEth(address to, uint256 amount) external {
    (bool success, ) = to.call{value: amount}("");
    require(success, "ETH transfer failed");
}
```

### Wrong: Using tx.origin for authentication

```solidity
// BAD -- SWC-115: vulnerable to phishing via relay contract
require(tx.origin == owner, "Not authorized");
```

### Correct: Use msg.sender for authentication

```solidity
// GOOD -- msg.sender cannot be spoofed by intermediate contracts
require(msg.sender == owner, "Not authorized");
```

### Wrong: No reentrancy guard on fund-moving function

```solidity
// BAD -- state update after external call
function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok);
    balances[msg.sender] -= amount;  // too late!
}
```

### Correct: Checks-Effects-Interactions pattern

```solidity
// GOOD -- state update before external call + reentrancy guard
function withdraw(uint256 amount) external nonReentrant {
    require(balances[msg.sender] >= amount, "Insufficient");
    balances[msg.sender] -= amount;  // effect first
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok, "Transfer failed");
}
```

### Wrong: Unbounded loop over dynamic array (DoS)

```solidity
// BAD -- SWC-113: array grows unbounded, eventually hits gas limit
function distributeRewards() external {
    for (uint i = 0; i < recipients.length; i++) {
        payable(recipients[i]).transfer(rewards[i]);
    }
}
```

### Correct: Pull-over-push payment pattern

```solidity
// GOOD -- each user withdraws their own rewards
mapping(address => uint256) public pendingRewards;

function claimReward() external {
    uint256 reward = pendingRewards[msg.sender];
    require(reward > 0, "No reward");
    pendingRewards[msg.sender] = 0;
    (bool ok, ) = msg.sender.call{value: reward}("");
    require(ok, "Transfer failed");
}
```

## Common Pitfalls

- **Assuming Solidity 0.8.x eliminates all overflow risks**: Built-in checks only apply to normal arithmetic. Code inside `unchecked { }` blocks bypasses overflow protection intentionally. Audit every `unchecked` block manually. Fix: grep for `unchecked` and verify each usage is safe. [src6]
- **Trusting msg.value in loops**: In a loop, `msg.value` does not decrease per iteration -- the same ETH can be "spent" multiple times if not tracked. Fix: track remaining value in a local variable and decrement it. [src2]
- **ERC-20 approve race condition**: Changing an allowance from N to M allows a spender to front-run and spend N+M. Fix: use `increaseAllowance`/`decreaseAllowance` or approve to 0 first (required for USDT). [src5]
- **Assuming private state variables are secret**: All on-chain storage is publicly readable regardless of visibility modifier. Fix: never store secrets, passwords, or encryption keys in contract storage. [src2]
- **Forgetting to verify proxy storage layout**: Adding or reordering state variables in an upgradeable contract causes storage collision. Fix: use OpenZeppelin's `@openzeppelin/upgrades` plugin which checks storage layout automatically. [src3]
- **Ignoring compiler version pinning**: Using `pragma solidity ^0.8.0` means different auditors may compile with different versions, producing different bytecode. Fix: pin to exact version like `pragma solidity 0.8.20;`. [src6]
- **Not testing with forked mainnet state**: Unit tests with mock contracts miss real-world interactions (e.g., token decimals, fee-on-transfer tokens, rebasing tokens). Fix: use `forge test --fork-url` to test against production state. [src4]

## Diagnostic Commands

```bash
# Run Slither static analysis (all detectors)
slither . --json report.json

# Run Slither with specific high-severity detectors only
slither . --detect reentrancy-eth,reentrancy-no-eth,arbitrary-send-eth,controlled-delegatecall,unprotected-upgrade

# List all Slither detectors and their severity
slither . --list-detectors

# Run Mythril on a specific contract
myth analyze contracts/MyContract.sol --solv 0.8.20

# Run Mythril on deployed contract (requires RPC)
myth analyze --address 0xCONTRACT --rpc https://eth-mainnet.g.alchemy.com/v2/KEY

# Run Foundry fuzz tests (10k runs)
forge test --fuzz-runs 10000 -vvv

# Run Echidna property-based fuzzer
echidna . --contract MyContractTest --config echidna.yaml --test-mode assertion

# Check for known vulnerabilities in dependencies
npm audit
# or for Foundry
forge inspect MyContract storage-layout

# Verify contract source matches deployed bytecode
forge verify-contract --chain-id 1 --compiler-version 0.8.20 0xADDRESS contracts/MyContract.sol:MyContract

# Generate storage layout diff for proxy upgrades
forge inspect MyContractV1 storage-layout > v1.json
forge inspect MyContractV2 storage-layout > v2.json
diff v1.json v2.json
```

## Version History & Compatibility

| Solidity Version | Status | Key Security Changes | Migration Notes |
|---|---|---|---|
| 0.8.20+ | Current | Custom errors (gas savings), transient storage (EIP-1153) | Pin exact version; verify optimizer settings match audit |
| 0.8.0-0.8.19 | Supported | Built-in overflow/underflow checks, ABI coder v2 default | Remove SafeMath imports; review `unchecked` blocks |
| 0.7.x | Legacy | Last version requiring SafeMath | Upgrade to 0.8.x; remove SafeMath; test all arithmetic |
| 0.6.x | EOL | No overflow protection, different ABI encoding | Must upgrade; extensive re-audit required |
| 0.4.x-0.5.x | EOL | Many known vulnerabilities, no built-in protections | Full rewrite recommended |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Deploying contracts that handle user funds (DeFi, vaults, bridges) | Building a read-only contract with no value transfer | Basic unit testing is sufficient for view-only contracts |
| Launching a token (ERC-20, ERC-721, ERC-1155) | Writing a private/internal test contract on a devnet | Foundry unit tests for development testing |
| Implementing upgradeable proxy patterns | Contract is a simple wrapper around a well-audited library | Code review of the integration points only |
| Building cross-chain bridges or oracle integrations | Smart contract is on a non-EVM chain (Solana, Aptos) | Chain-specific audit tools (Anchor for Solana, Move Prover) |
| Post-hack incident response and forensic analysis | You need a gas optimization review only | Gas profiling tools (forge snapshot, hardhat-gas-reporter) |

## Important Caveats

- The SWC Registry has not been actively maintained since 2020. For current vulnerability classifications, reference the EEA EthTrust Security Levels specification (v2, Dec 2023) and the Smart Contract Security Verification Standard (SCSVS).
- No audit guarantees zero bugs. Audits reduce risk but cannot eliminate it entirely. Bug bounty programs (Immunefi, HackerOne) provide ongoing coverage.
- DeFi composability means your contract's security depends on every contract it interacts with. A secure contract calling an insecure oracle or token can still be exploited.
- Formal verification (Certora, K Framework) provides mathematical proofs for specific properties but cannot cover all possible attack vectors.
- Layer 2 chains (Optimism, Arbitrum, zkSync) may have different EVM opcodes or gas costs that affect security assumptions.

## Related Units

- [OWASP Top 10](/software/security/owasp-top-10/2026)
- [Error Handling Strategies](/software/patterns/error-handling-strategies/2026)
