# proxy-upgrade-safety

Detects vulnerabilities in upgradeable proxy smart contracts including storage layout collisions, uninitialized implementations, function selector clashing, delegatecall context issues, and upgrade path safety. Covers Transparent Proxy, UUPS (EIP-1822), Beacon, Diamond (EIP-2535), and Minimal Proxy (EIP-1167) patterns. Use when auditing upgradeable contracts, reviewing implementation upgrades, analyzing delegatecall architectures, or verifying proxy pattern compliance.

- **Kind:** skill
- **Source:** https://github.com/quillai-network/qs_skills
- **Page:** https://forefy.com/skills/afcf6585-6c78-475e-92e1-f9c01ea93838
- **API (JSON + files):** https://forefy.com/api/asr/afcf6585-6c78-475e-92e1-f9c01ea93838

---

## SKILL.md

---
name: proxy-upgrade-safety
description: Detects vulnerabilities in upgradeable proxy smart contracts including storage layout collisions, uninitialized implementations, function selector clashing, delegatecall context issues, and upgrade path safety. Covers Transparent Proxy, UUPS (EIP-1822), Beacon, Diamond (EIP-2535), and Minimal Proxy (EIP-1167) patterns. Use when auditing upgradeable contracts, reviewing implementation upgrades, analyzing delegatecall architectures, or verifying proxy pattern compliance.
---

# Proxy & Upgrade Safety

Detect vulnerabilities specific to **upgradeable proxy architectures** — the most widely deployed contract pattern on Ethereum (54.2% of contracts). Proxy bugs cause storage corruption, unauthorized upgrades, and complete contract takeover.

## When to Use

- Auditing any contract using proxy/implementation pattern (Transparent, UUPS, Beacon, Diamond)
- Reviewing implementation contract upgrades for storage layout compatibility
- Analyzing `delegatecall`-based architectures and library usage
- Verifying initialization safety (can `initialize()` be front-run?)
- Checking Diamond (EIP-2535) facet management for selector collisions

## When NOT to Use

- Non-upgradeable contracts without proxy patterns
- Pure logic audits without proxy architecture (use behavioral-state-analysis)
- Token standard compliance (use external-call-safety)

## Core Concept: The Delegatecall Storage Model

When Proxy calls Implementation via `delegatecall`:

```
┌─────────────────────┐     delegatecall     ┌─────────────────────┐
│       PROXY         │ ──────────────────→   │   IMPLEMENTATION    │
│                     │                       │                     │
│ Storage:            │  Implementation code  │ Code only:          │
│   slot 0: admin     │  executes in proxy's  │   No persistent     │
│   slot 1: impl addr │  storage context      │   storage           │
│   slot 2: user data │                       │                     │
│   slot 3: user data │                       │                     │
└─────────────────────┘                       └─────────────────────┘
```

**Key Rule:** The implementation's code reads/writes the PROXY's storage slots. If storage layouts don't match, data corruption occurs.

## Five Vulnerability Classes

### Class 1: Storage Layout Collision

**Between Proxy and Implementation:**

```solidity
// Proxy contract
contract Proxy {
    address public admin;           // slot 0
    address public implementation;  // slot 1

    fallback() external payable {
        delegatecall(implementation);
    }
}

// Implementation contract
contract ImplementationV1 {
    uint256 public totalSupply;     // slot 0 — COLLIDES with admin!
    mapping(address => uint256) public balances; // slot 1 — COLLIDES with implementation!
}
```

**Detection:** Compare storage slot assignments between proxy and implementation. Any overlap = CRITICAL vulnerability.

**Between Implementation Versions:**

```solidity
// V1
contract ImplementationV1 {
    uint256 public totalSupply;     // slot 0
    address public owner;           // slot 1
    mapping(address => uint256) balances; // slot 2
}

// V2 — DANGEROUS: inserted variable before existing ones
contract ImplementationV2 {
    bool public paused;             // slot 0 — COLLIDES with totalSupply!
    uint256 public totalSupply;     // slot 1 — COLLIDES with owner!
    address public owner;           // slot 2 — COLLIDES with balances!
    mapping(address => uint256) balances; // slot 3
}
```

**Safe V2:**

```solidity
contract ImplementationV2 {
    uint256 public totalSupply;     // slot 0 — same
    address public owner;           // slot 1 — same
    mapping(address => uint256) balances; // slot 2 — same
    bool public paused;             // slot 3 — NEW, appended at end
}
```

### Class 2: Uninitialized Implementation

Proxy pattern uses `initialize()` instead of `constructor()`. If the implementation contract itself is not initialized, an attacker can call `initialize()` directly on it.

```solidity
contract ImplementationV1 is Initializable {
    address public owner;

    function initialize(address _owner) external initializer {
        owner = _owner;
    }

    function selfDestruct() external {
        require(msg.sender == owner);
        selfdestruct(payable(msg.sender));
    }
}
```

**Attack:**

```
1. Implementation deployed but initialize() not called on impl itself
2. Attacker calls implementation.initialize(attacker_address)
3. Attacker is now owner of the IMPLEMENTATION contract
4. Attacker calls selfDestruct() on implementation
5. Proxy now delegatecalls to destroyed contract
6. ALL proxy calls return empty data — contract bricked
```

**Detection:**

```
For each implementation contract:
  1. Does it have initialize() or any initializer function?
  2. Was initialize() called on the implementation address (not just the proxy)?
  3. Does the constructor call _disableInitializers()?
  4. If no → UNINITIALIZED IMPLEMENTATION vulnerability
```

### Class 3: Function Selector Clashing

Solidity function selectors are only 4 bytes. Collisions between proxy admin functions and implementation functions cause unexpected behavior.

```solidity
// Proxy has admin function
function upgrade(address newImpl) external;  // selector: 0x0900f010

// Implementation has user function with SAME selector
function collide(uint256 amount) external;   // selector: 0x0900f010

// When user calls collide(), proxy intercepts it as upgrade()!
```

**Transparent Proxy Mitigation:** Admin can only call admin functions; users can only call implementation functions. But this must be correctly implemented.

**Detection:**

```
For each function in the proxy:
  selector_proxy = keccak256(signature)[:4]
  For each function in the implementation:
    selector_impl = keccak256(signature)[:4]
    If selector_proxy == selector_impl:
      → FUNCTION SELECTOR CLASH
```

### Class 4: Missing Upgrade Authorization

**UUPS Pattern:** The upgrade logic lives in the implementation, not the proxy. If `_authorizeUpgrade()` is not properly protected, anyone can upgrade.

```solidity
// VULNERABLE: Missing access control on upgrade
contract ImplementationV1 is UUPSUpgradeable {
    function _authorizeUpgrade(address newImplementation) internal override {
        // NO ACCESS CHECK! Anyone can upgrade!
    }
}

// SAFE
contract ImplementationV1 is UUPSUpgradeable, OwnableUpgradeable {
    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
        // Only owner can upgrade
    }
}
```

**Detection:**

```
For UUPS proxies:
  1. Find _authorizeUpgrade() function
  2. Check for access control (onlyOwner, onlyRole, require(msg.sender == admin))
  3. If no access control → CRITICAL: unauthorized upgrade
  4. Also check: Can _authorizeUpgrade be removed in a new version?
     → If V2 doesn't inherit UUPSUpgradeable → proxy becomes non-upgradeable (bricked)
```

### Class 5: Delegatecall Context Confusion

Code executing via `delegatecall` runs with the caller's `msg.sender`, `msg.value`, and storage. Misunderstanding this context creates vulnerabilities.

```solidity
// Implementation stores admin in its own constructor
contract Implementation {
    address public admin;

    constructor() {
        admin = msg.sender; // Sets admin in IMPLEMENTATION storage
        // When called via delegatecall, this is proxy's storage
        // BUT constructor only runs during deployment, not via proxy!
    }
}
```

**Key Rule:** Constructors NEVER run via delegatecall. Any state set in the constructor exists only in the implementation's own storage, not the proxy's.

## Three-Phase Detection Architecture

### Phase 1: Proxy Pattern Classification

Identify which proxy pattern is used.

| Pattern | Key Indicator | Upgrade Location |
|---------|--------------|-----------------|
| Transparent (EIP-1967) | `_IMPLEMENTATION_SLOT` at `keccak256('eip1967.proxy.implementation') - 1` | Proxy contract |
| UUPS (EIP-1822) | `proxiableUUID()` in implementation | Implementation contract |
| Beacon | `_BEACON_SLOT` at `keccak256('eip1967.proxy.beacon') - 1` | Beacon contract |
| Diamond (EIP-2535) | `diamondCut()` function, facet registry | Diamond contract |
| Minimal (EIP-1167) | Clone bytecode pattern `363d3d373d3d3d363d73...` | Not upgradeable |

### Phase 2: Storage Layout Analysis

Build the complete storage map for proxy and all implementation versions.

**Algorithm:**

```
For each contract C (proxy, impl_v1, impl_v2, ...):
  storage_map[C] = {}
  slot = 0
  For each state variable V in C (in declaration order):
    storage_map[C][slot] = V
    slot += size_of(V)  // Consider packing for <32 byte types

For each slot S:
  If storage_map[proxy][S] conflicts with storage_map[impl][S]:
    → PROXY-IMPL COLLISION at slot S
  If storage_map[impl_v1][S] != storage_map[impl_v2][S]:
    → UPGRADE COLLISION at slot S
```

**Special Cases:**

- Mappings and dynamic arrays: hash-based slot calculation
- Struct packing: multiple variables per slot
- Inherited contracts: storage order follows C3 linearization
- Gap variables (`uint256[50] private __gap`): reserved space for upgrades

### Phase 3: Initialization & Upgrade Path Verification

```
Initialization Checks:
  1. Does implementation use Initializable?
  2. Is initialize() protected by initializer modifier?
  3. Does constructor call _disableInitializers()?
  4. Can initialize() be called more than once? (reinitializer)
  5. Was initialize() called on impl address directly?

Upgrade Path Checks:
  1. Is upgrade function access-controlled?
  2. Does new impl maintain storage layout compatibility?
  3. Does new impl still support upgrades? (UUPS: must inherit UUPSUpgradeable)
  4. Is there a timelock on upgrades?
  5. Can upgrade + initialize race condition occur?
```

## Workflow

```
Task Progress:
- [ ] Step 1: Identify proxy pattern (Transparent, UUPS, Beacon, Diamond, Minimal)
- [ ] Step 2: Map storage layout of proxy contract
- [ ] Step 3: Map storage layout of all implementation versions
- [ ] Step 4: Check for storage collisions (proxy-impl and version-version)
- [ ] Step 5: Verify initialization safety (disableInitializers, initializer modifier)
- [ ] Step 6: Check function selector clashing (proxy admin vs impl functions)
- [ ] Step 7: Verify upgrade authorization (access control on upgrade path)
- [ ] Step 8: Check delegatecall context safety
- [ ] Step 9: Score findings and generate report
```

## Output Format

```markdown
## Proxy & Upgrade Safety Report

### Finding: [Title]

**Contract:** `ContractName` at `Contract.sol:L42`
**Proxy Pattern:** [Transparent | UUPS | Beacon | Diamond | Minimal]
**Class:** [Storage Collision | Uninitialized Impl | Selector Clash | Missing Auth | Context Confusion]
**Severity:** [CRITICAL | HIGH | MEDIUM]

**Issue:**
[Description of the proxy-specific vulnerability]

**Storage Layout:**
  Proxy slot 0: `[proxy variable]`
  Impl  slot 0: `[impl variable]` ← COLLISION

**Attack Scenario:**
1. [Step-by-step exploit]

**Impact:**
[Storage corruption, unauthorized upgrade, contract bricked, etc.]

**Recommendation:**
[Use EIP-1967 slots, add _disableInitializers, add access control, append-only storage]
```

## Quick Detection Checklist

- [ ] Does the proxy store admin/implementation at standard EIP-1967 slots (not regular slots)?
- [ ] Does the implementation's `constructor()` call `_disableInitializers()`?
- [ ] Does `initialize()` use the `initializer` modifier?
- [ ] Do implementation upgrades ONLY append new state variables (never insert or reorder)?
- [ ] Is there a `__gap` variable for future storage expansion in base contracts?
- [ ] For UUPS: Does `_authorizeUpgrade()` have proper access control?
- [ ] For UUPS: Does every new implementation still inherit `UUPSUpgradeable`?
- [ ] Are there any function selector collisions between proxy and implementation?
- [ ] Is there a timelock or multisig on the upgrade path?

For proxy pattern details, see [{baseDir}/references/proxy-patterns.md]({baseDir}/references/proxy-patterns.md).
For storage collision detection, see [{baseDir}/references/storage-collision-detection.md]({baseDir}/references/storage-collision-detection.md).

## Rationalizations to Reject

- "We use OpenZeppelin's proxy" → OZ provides the framework, but storage layout compatibility is YOUR responsibility
- "The implementation is initialized" → Was it initialized on the IMPLEMENTATION address, or only through the proxy?
- "Constructor sets the admin" → Constructors don't run via delegatecall; admin is only set in impl's own storage
- "We tested the upgrade" → Did you verify storage layout slot-by-slot? One reordered variable corrupts everything
- "UUPS is safer than Transparent" → Only if `_authorizeUpgrade` is properly protected AND maintained across upgrades
- "The gap variable protects us" → Only if inherited contracts also have gaps and you never exceed the gap size

## references

```

```

## references/proxy-patterns.md

# Proxy Patterns — Detailed Comparison

## Pattern 1: Transparent Proxy (EIP-1967)

### Architecture

```
┌──────────────┐     delegatecall      ┌──────────────────┐
│    Proxy     │ ───────────────────→   │  Implementation  │
│              │                        │                  │
│ EIP-1967     │  Admin calls:          │  Business logic  │
│ storage slots│  → handled by proxy    │  No upgrade logic│
│              │  User calls:           │                  │
│              │  → delegated to impl   │                  │
└──────────────┘                        └──────────────────┘
```

### Storage Slots (EIP-1967)

```solidity
// Implementation address stored at:
bytes32 constant IMPLEMENTATION_SLOT =
    bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);
// = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

// Admin address stored at:
bytes32 constant ADMIN_SLOT =
    bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1);
// = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
```

### How It Works

```solidity
contract TransparentProxy {
    fallback() external payable {
        if (msg.sender == admin) {
            // Admin functions: upgrade, changeAdmin
            _handleAdmin();
        } else {
            // User functions: delegate to implementation
            _delegate(implementation);
        }
    }
}
```

### Security Properties

| Property | Status | Notes |
|----------|--------|-------|
| Storage collision prevention | YES | EIP-1967 uses hashed slots |
| Admin/user separation | YES | Admin can't call impl functions |
| Upgrade authorization | Proxy-controlled | Admin address in proxy |
| Self-destruct risk | LOW | Upgrade in proxy, not impl |
| Gas overhead | HIGHER | Admin check on every call |

### Vulnerability Checklist

- [ ] Is admin stored at EIP-1967 slot (not slot 0)?
- [ ] Can admin accidentally call implementation functions? (Should NOT be possible)
- [ ] Is admin address a multisig or governance contract?
- [ ] Can admin be changed? Is `changeAdmin()` access-controlled?

---

## Pattern 2: UUPS (EIP-1822)

### Architecture

```
┌──────────────┐     delegatecall      ┌──────────────────┐
│    Proxy     │ ───────────────────→   │  Implementation  │
│              │                        │                  │
│ Minimal proxy│  ALL calls delegated   │  Business logic  │
│ No admin     │  to implementation     │  + upgrade logic │
│ logic        │                        │  _authorizeUpgrade│
└──────────────┘                        └──────────────────┘
```

### Key Difference from Transparent

Upgrade logic lives in the **implementation**, not the proxy. This makes the proxy simpler and cheaper, but introduces new risks.

### Security Properties

| Property | Status | Notes |
|----------|--------|-------|
| Storage collision prevention | YES | EIP-1967 slots |
| Upgrade authorization | **Implementation-controlled** | Must be in every version |
| Self-destruct risk | **HIGH** | If impl has selfdestruct, proxy breaks |
| Gas overhead | LOWER | No admin check per call |
| Upgrade continuity | **RISKY** | New impl MUST inherit UUPSUpgradeable |

### Critical Risks

```solidity
// RISK 1: Missing access control
function _authorizeUpgrade(address) internal override {
    // No check! Anyone can upgrade!
}

// RISK 2: Forgetting UUPSUpgradeable in new version
contract V2 { // Does NOT inherit UUPSUpgradeable
    // Proxy can never be upgraded again — BRICKED
}

// RISK 3: Selfdestruct on implementation
contract V1 is UUPSUpgradeable {
    function destroy() external onlyOwner {
        selfdestruct(payable(owner));
        // Destroys implementation → proxy broken forever
    }
}
```

### Vulnerability Checklist

- [ ] Does `_authorizeUpgrade()` have access control (onlyOwner/onlyRole)?
- [ ] Does every implementation version inherit `UUPSUpgradeable`?
- [ ] Is there a `selfdestruct` or `delegatecall` in the implementation?
- [ ] Does the constructor call `_disableInitializers()`?
- [ ] Can the upgrade function be front-run during deployment?

---

## Pattern 3: Beacon Proxy

### Architecture

```
┌──────────┐     ┌──────────┐     ┌──────────────────┐
│ Proxy A  │──→  │  Beacon  │──→  │  Implementation  │
├──────────┤     │          │     │                  │
│ Proxy B  │──→  │ Returns  │     │  Shared logic    │
├──────────┤     │ impl     │     │  for all proxies │
│ Proxy C  │──→  │ address  │     │                  │
└──────────┘     └──────────┘     └──────────────────┘
```

### How It Works

Multiple proxies point to a single beacon. The beacon stores the implementation address. Upgrading the beacon upgrades ALL proxies simultaneously.

### Security Properties

| Property | Status | Notes |
|----------|--------|-------|
| Batch upgrade | YES | One beacon update → all proxies upgraded |
| Individual proxy upgrade | NO | All proxies share same impl |
| Beacon authorization | CRITICAL | Must be tightly controlled |
| Gas overhead | MEDIUM | Extra SLOAD for beacon address |

### Vulnerability Checklist

- [ ] Who controls the beacon? Is it a multisig/governance?
- [ ] Can individual proxies be pointed to a different beacon?
- [ ] Is the beacon upgrade timelocked?
- [ ] What happens if the beacon is destroyed?

---

## Pattern 4: Diamond (EIP-2535)

### Architecture

```
┌──────────────┐     ┌─────────────────────────────┐
│   Diamond    │     │  Facets (multiple impls)     │
│              │     │                              │
│ Function     │     │  Facet A: functions 1-5      │
│ selector →   │──→  │  Facet B: functions 6-10     │
│ facet map    │     │  Facet C: functions 11-15    │
│              │     │                              │
│ diamondCut() │     │  Each facet has own code     │
└──────────────┘     └─────────────────────────────┘
```

### Security Properties

| Property | Status | Notes |
|----------|--------|-------|
| Granular upgrades | YES | Individual functions upgradeable |
| Selector management | COMPLEX | Must track selector→facet mapping |
| Storage management | COMPLEX | App storage or diamond storage pattern |
| Size limit bypass | YES | No 24KB contract limit |

### Vulnerability Checklist

- [ ] Is `diamondCut()` access-controlled?
- [ ] Can selectors collide between facets?
- [ ] Is storage shared safely between facets? (Diamond Storage vs App Storage)
- [ ] Can a facet's `selfdestruct` affect other facets?
- [ ] Is there a loupe facet for introspection?

---

## Pattern 5: Minimal Proxy (EIP-1167 Clone)

### Architecture

```
┌──────────────────────────────────────┐
│ Clone (45 bytes of bytecode)         │
│ 363d3d373d3d3d363d73{impl}5af43d82  │
│                                      │
│ Hardcoded implementation address     │
│ NOT upgradeable                      │
└──────────────────────────────────────┘
```

### Security Notes

- **Not upgradeable** — implementation address is hardcoded in bytecode
- **Cheap to deploy** — only 45 bytes
- **Shares implementation** — all clones use same code
- **Independent storage** — each clone has own storage

### Vulnerability Checklist

- [ ] Is the implementation contract safe from selfdestruct?
- [ ] Is the implementation properly initialized?
- [ ] Does each clone properly initialize its own state?

---

## Cross-Pattern Comparison

| Feature | Transparent | UUPS | Beacon | Diamond | Minimal |
|---------|------------|------|--------|---------|---------|
| Upgradeable | YES | YES | YES | YES | NO |
| Upgrade location | Proxy | Implementation | Beacon | Diamond | N/A |
| Storage collision risk | LOW (EIP-1967) | LOW (EIP-1967) | LOW | MEDIUM | LOW |
| Self-destruct risk | LOW | **HIGH** | LOW | MEDIUM | MEDIUM |
| Gas per call | Higher | Lower | Medium | Medium | Lowest |
| Complexity | Medium | Medium | Medium | **High** | Low |
| Multiple impls | NO | NO | NO | **YES** | NO |
| Batch upgrade | NO | NO | **YES** | NO | NO |

## references/storage-collision-detection.md

# Storage Collision Detection — Algorithm Reference

## Solidity Storage Layout Rules

### Basic Types

| Type | Size | Slots Used |
|------|------|------------|
| `bool` | 1 byte | 1 (packed with adjacent small types) |
| `uint8`-`uint256` | 1-32 bytes | 1 slot for 32 bytes; smaller types pack |
| `int8`-`int256` | 1-32 bytes | Same as uint |
| `address` | 20 bytes | 1 (can pack with 12 bytes of others) |
| `bytes1`-`bytes32` | 1-32 bytes | 1 slot |
| `enum` | 1 byte (usually) | Packed |

### Complex Types

| Type | Slot Calculation |
|------|-----------------|
| Fixed array `T[N]` | N consecutive slots (or packed) |
| Dynamic array `T[]` | Length at slot `p`; elements at `keccak256(p) + i` |
| `mapping(K => V)` | Slot `p` unused; value at `keccak256(k . p)` |
| `struct` | Members packed sequentially starting at struct's slot |
| `string` / `bytes` | Short (≤31 bytes): stored in slot `p`; Long: length at `p`, data at `keccak256(p)` |

### Packing Rules

Variables are packed into a single 32-byte slot when possible:

```solidity
contract Packed {
    uint128 a;  // slot 0, bytes 0-15
    uint128 b;  // slot 0, bytes 16-31  (packed with a)
    uint256 c;  // slot 1 (too large to pack)
    uint8 d;    // slot 2, byte 0
    address e;  // slot 2, bytes 1-20 (packed with d)
    bool f;     // slot 2, byte 21 (packed with d and e)
    uint256 g;  // slot 3
}
```

---

## Storage Layout Extraction Algorithm

### Step 1: Parse Contract Hierarchy

```
For contract C:
  1. Resolve inheritance chain via C3 linearization
  2. Process state variables in order: base → derived
  3. Include all inherited contracts' state variables

Example:
  contract A { uint256 x; }           // slot 0
  contract B is A { uint256 y; }      // slot 1
  contract C is B { uint256 z; }      // slot 2

  C's layout: [x @ slot 0, y @ slot 1, z @ slot 2]
```

### Step 2: Build Slot Map

```python
def build_storage_layout(contract):
    layout = {}
    current_slot = 0
    current_offset = 0  # bytes within current slot

    for var in contract.state_variables_in_order():
        size = get_byte_size(var.type)

        # Check if variable fits in current slot
        if current_offset + size > 32:
            current_slot += 1
            current_offset = 0

        layout[var.name] = {
            'slot': current_slot,
            'offset': current_offset,
            'size': size,
            'type': var.type
        }

        # Special handling for complex types
        if is_dynamic_array(var.type) or is_mapping(var.type):
            current_slot += 1  # These take a full slot (length or unused)
            current_offset = 0
        elif is_struct(var.type):
            current_slot += struct_slots(var.type)
            current_offset = 0
        else:
            current_offset += size
            if current_offset >= 32:
                current_slot += 1
                current_offset = 0

    return layout
```

### Step 3: Compare Layouts

```python
def detect_collisions(layout_a, layout_b):
    collisions = []

    for var_a_name, var_a in layout_a.items():
        for var_b_name, var_b in layout_b.items():
            if var_a['slot'] == var_b['slot']:
                # Same slot — check for overlap
                a_start = var_a['offset']
                a_end = a_start + var_a['size']
                b_start = var_b['offset']
                b_end = b_start + var_b['size']

                if a_start < b_end and b_start < a_end:
                    # Overlapping bytes in same slot
                    if var_a['type'] != var_b['type'] or var_a_name != var_b_name:
                        collisions.append({
                            'slot': var_a['slot'],
                            'var_a': var_a_name,
                            'var_b': var_b_name,
                            'type_a': var_a['type'],
                            'type_b': var_b['type'],
                            'severity': classify_severity(var_a, var_b)
                        })

    return collisions
```

---

## Collision Severity Classification

| Collision Type | Severity | Impact |
|---------------|----------|--------|
| Admin/owner slot vs user data | CRITICAL | Attacker can overwrite admin |
| Implementation slot vs user data | CRITICAL | Attacker can change implementation |
| Financial variable vs any variable | CRITICAL | Balance/supply corruption |
| Same-type reordering | HIGH | Data read from wrong variable |
| Type mismatch (same semantics) | HIGH | Truncation, misinterpretation |
| Gap variable collision | MEDIUM | Reserved space violated |
| Metadata collision | LOW | Non-critical data corruption |

---

## Gap Pattern for Safe Upgrades

### Standard Gap Pattern

```solidity
contract BaseContractV1 {
    uint256 public value;
    address public admin;

    // Reserve 50 slots for future variables
    uint256[48] private __gap; // 50 - 2 used = 48 remaining
}

contract BaseContractV2 {
    uint256 public value;      // slot 0 — unchanged
    address public admin;      // slot 1 — unchanged
    bool public paused;        // slot 2 — NEW (was first __gap slot)

    uint256[47] private __gap; // 50 - 3 used = 47 remaining
}
```

### Gap Verification Algorithm

```python
def verify_gap_safety(v1_layout, v2_layout):
    # 1. All V1 variables must be in same position in V2
    for var_name, var_v1 in v1_layout.items():
        if var_name == '__gap':
            continue
        if var_name not in v2_layout:
            return Error(f"Variable {var_name} removed in V2")
        var_v2 = v2_layout[var_name]
        if var_v1['slot'] != var_v2['slot']:
            return Error(f"Variable {var_name} moved from slot {var_v1['slot']} to {var_v2['slot']}")
        if var_v1['type'] != var_v2['type']:
            return Error(f"Variable {var_name} type changed: {var_v1['type']} → {var_v2['type']}")

    # 2. New variables must be in gap space or after all V1 slots
    # 3. Gap must be reduced by exactly the number of new slots used
    # 4. Total slots (variables + gap) must remain constant
```

---

## EIP-1967 Slot Verification

### Standard Slots

```solidity
// Implementation slot
keccak256("eip1967.proxy.implementation") - 1
= 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

// Admin slot
keccak256("eip1967.proxy.admin") - 1
= 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103

// Beacon slot
keccak256("eip1967.proxy.beacon") - 1
= 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
```

### Verification

```
For each proxy contract:
  1. Check if implementation is stored at EIP-1967 slot (not slot 0/1/2)
  2. If stored at regular slot → HIGH RISK of collision with implementation variables
  3. Verify admin is stored at EIP-1967 admin slot
  4. For beacon proxies: verify beacon at EIP-1967 beacon slot
```

---

## Common Collision Scenarios

### Scenario 1: Inherited Contract Reordering

```solidity
// V1
contract V1 is OwnableUpgradeable, PausableUpgradeable {
    uint256 public value; // After Ownable + Pausable slots
}

// V2 — DANGEROUS: swapped inheritance order
contract V2 is PausableUpgradeable, OwnableUpgradeable {
    uint256 public value; // Ownable and Pausable slots are now different!
}
```

### Scenario 2: Struct Modification

```solidity
// V1
struct UserInfo {
    uint256 balance;
    uint256 lastUpdate;
}

// V2 — DANGEROUS: added field in middle of struct
struct UserInfo {
    uint256 balance;
    address token;      // NEW — shifts lastUpdate
    uint256 lastUpdate; // Now at wrong slot!
}
```

### Scenario 3: Enum Expansion

```solidity
// V1
enum Status { Active, Paused }  // 0, 1

// V2 — DANGEROUS if inserted before existing values
enum Status { Pending, Active, Paused }  // Pending=0, Active=1, Paused=2
// All stored Active (1) values now mean "Active" but were stored as Active in V1
// If insertion is at start, 0 (was Active) now means Pending
```

**Safe enum expansion:** Always append new values at the end.

