## Contents

- 2. Vulnerability Checklist
- 2.1 Reentrancy
- 2.2 Oracle Manipulation / Price Feed Attacks
- 2.3 Flash Loan Attack Vectors
- 2.4 Storage Collisions in Proxies
- 2.5 Front-Running / Sandwich Attacks / MEV
- 2.6 Access Control Issues
- 2.7 Integer Overflow/Underflow
- 2.8 Unchecked External Calls
- 2.9 Denial of Service Patterns

## 2. Vulnerability Checklist

### 2.1 Reentrancy

**Vulnerable:**
```solidity
function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok);
    balances[msg.sender] -= amount; // STATE AFTER CALL — reentrancy
}
```

**Fixed (CEI Pattern):**
```solidity
function withdraw(uint256 amount) external nonReentrant {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;       // EFFECTS first
    (bool ok, ) = msg.sender.call{value: amount}(""); // INTERACTION last
    require(ok);
}
```

Cross-function reentrancy: check if any two functions share state and one has an external call before state update.

### 2.2 Oracle Manipulation / Price Feed Attacks

**Vulnerable (spot price):**
```solidity
function getPrice() public view returns (uint256) {
    (uint112 r0, uint112 r1, ) = pair.getReserves();
    return (uint256(r1) * 1e18) / uint256(r0); // manipulable in same tx
}
```

**Fixed (Chainlink + staleness check):**
```solidity
function getPrice() public view returns (uint256) {
    (, int256 answer, , uint256 updatedAt, ) = priceFeed.latestRoundData();
    require(answer > 0, "invalid price");
    require(block.timestamp - updatedAt < 3600, "stale price");
    return uint256(answer);
}
```

Also consider TWAP for on-chain pricing:
```solidity
// Uniswap V3 TWAP — use OracleLibrary.consult(pool, twapInterval)
```

### 2.3 Flash Loan Attack Vectors

Audit checks:
- Can any single-tx deposit + action + withdraw exploit state?
- Are governance votes protected by minimum holding periods?
- Are liquidity-based calculations snapshottable in one block?

**Guard pattern:**
```solidity
mapping(address => uint256) public lastDepositBlock;

function deposit() external {
    lastDepositBlock[msg.sender] = block.number;
    // ...
}

function vote() external {
    require(block.number > lastDepositBlock[msg.sender], "same block");
    // ...
}
```

### 2.4 Storage Collisions in Proxies

**Problem:** Proxy and implementation share storage. Misaligned slots corrupt data.

```solidity
// Implementation V1
contract V1 {
    uint256 public value;    // slot 0
    address public owner;    // slot 1
}

// Implementation V2 — WRONG: inserted variable shifts slots
contract V2 {
    uint256 public value;    // slot 0
    uint256 public newVar;   // slot 1 — COLLISION with owner!
    address public owner;    // slot 2
}

// Implementation V2 — CORRECT: append only
contract V2 {
    uint256 public value;    // slot 0
    address public owner;    // slot 1
    uint256 public newVar;   // slot 2 — safe, appended
}
```

Use `forge inspect ContractName storage-layout` to verify slot alignment between versions.

### 2.5 Front-Running / Sandwich Attacks / MEV

**Vulnerable swap:**
```solidity
function swap(uint256 amountIn) external {
    router.swapExactTokensForTokens(amountIn, 0, path, msg.sender, block.timestamp);
    // amountOutMin = 0 allows sandwich
}
```

**Fixed:**
```solidity
function swap(uint256 amountIn, uint256 minOut, uint256 deadline) external {
    require(block.timestamp <= deadline, "expired");
    router.swapExactTokensForTokens(amountIn, minOut, path, msg.sender, deadline);
}
```

For sensitive operations, use commit-reveal:
```solidity
mapping(bytes32 => uint256) public commits;

function commit(bytes32 hash) external { commits[hash] = block.number; }

function reveal(uint256 value, bytes32 salt) external {
    bytes32 h = keccak256(abi.encodePacked(value, salt, msg.sender));
    require(commits[h] > 0 && block.number > commits[h] + 1, "too early");
    delete commits[h];
    _execute(value);
}
```

### 2.6 Access Control Issues

**Vulnerable (tx.origin):**
```solidity
function withdraw() external {
    require(tx.origin == owner); // phishing attack via malicious contract
}
```

**Fixed:**
```solidity
function withdraw() external {
    require(msg.sender == owner); // or use OpenZeppelin Ownable/AccessControl
}
```

Check for:
- Missing access modifiers on admin functions
- Single-step ownership transfer (use Ownable2Step)
- DEFAULT_ADMIN_ROLE granted too broadly
- Functions that should be `onlyOwner` but are `public`

### 2.7 Integer Overflow/Underflow

**Pre-0.8.0 (vulnerable):**
```solidity
// Solidity <0.8.0
uint8 balance = 255;
balance += 1; // wraps to 0 silently

// Fix: use SafeMath
balance = balance.add(1); // reverts on overflow
```

**Post-0.8.0:** Built-in overflow checks. But `unchecked {}` blocks bypass them:
```solidity
unchecked {
    uint8 x = 255;
    x += 1; // wraps to 0 — intentional? Audit this.
}
```

Audit every `unchecked` block. Verify the math genuinely cannot overflow.

### 2.8 Unchecked External Calls

**Vulnerable:**
```solidity
payable(to).send(amount); // return value ignored — funds may not arrive
token.transfer(to, amount); // non-standard tokens may return false
```

**Fixed:**
```solidity
(bool ok, ) = payable(to).call{value: amount}("");
require(ok, "ETH transfer failed");

// For ERC20:
SafeERC20.safeTransfer(token, to, amount);
```

Also check: `delegatecall` return values, low-level `call` without length check.

### 2.9 Denial of Service Patterns

**Unbounded loop (gas griefing):**
```solidity
// VULNERABLE: attacker adds thousands of entries
function distributeRewards() external {
    for (uint i = 0; i < recipients.length; i++) {
        token.transfer(recipients[i], rewards[i]); // OOG if array is huge
    }
}
```

**Fixed (pull pattern):**
```solidity
mapping(address => uint256) public pendingRewards;

function claimReward() external {
    uint256 amount = pendingRewards[msg.sender];
    pendingRewards[msg.sender] = 0;
    token.safeTransfer(msg.sender, amount);
}
```

Other DoS vectors:
- External call in loop (one revert blocks all)
- Block gas limit reached via large array iteration
- Griefing via forced revert in `receive()` / `fallback()`

---
