# audit-clm

Audits Solidity concentrated liquidity manager (CLM) protocols for vulnerabilities including forced unfavorable liquidity deployment via missing TWAP checks, owner rug-pull via TWAP parameter manipulation, tokens permanently stuck from rounding errors, stale token approvals after router updates, and retrospective fee application on previously earned rewards (project)

- **Kind:** skill
- **Source:** https://github.com/auditmos/skills
- **Page:** https://forefy.com/skills/608cab0a-eeab-4a38-ab7c-4c357e7c78d3
- **API (JSON + files):** https://forefy.com/api/asr/608cab0a-eeab-4a38-ab7c-4c357e7c78d3

---

## SKILL.md

---
name: audit-clm
description: Audits Solidity concentrated liquidity manager (CLM) protocols for vulnerabilities including forced unfavorable liquidity deployment via missing TWAP checks, owner rug-pull via TWAP parameter manipulation, tokens permanently stuck from rounding errors, stale token approvals after router updates, and retrospective fee application on previously earned rewards (project)
allowed-tools: Read, Grep, Glob
license: MIT
compatibility: Designed for Claude Code (or similar products)
metadata:
  author: Tomasz Kowalczyk (tom@auditmos.com)
  version: "1.0"
---

# Concentrated Liquidity Manager Auditor

## When to Use
- Auditing CLM protocols, Uniswap V3 position managers, liquidity management
- User mentions: CLM, concentrated liquidity, Uniswap V3, rebalance, TWAP, maxDeviation, position manager, liquidity deployment
- Analyzing liquidity rebalancing, TWAP protections, fee collection
- Reviewing router integrations, approval management

## Audit Workflow

**IMPORTANT: Announce skill usage at the start of analysis**

Begin with: "I'm using the **audit-clm** skill to analyze this contract for concentrated liquidity manager vulnerabilities..."

1. **Scan for CLM operations**
   - Search: `rebalance`, `mint`, `addLiquidity`, `TWAP`, `maxDeviation`, `twapInterval`, `router`, `approve`, `collectFees`, `protocolFee`
   - Focus: liquidity deployment, TWAP validation, approval management, fee updates

2. **Check against vulnerability patterns**
   - Reference `reference.md` for complete checklist
   - Compare code against `example.md`

3. **Validate exploitability**
   - **Check access control first** - grep for `onlyOwner|onlyAdmin|onlyGovernance` modifiers
   - Can non-privileged actors exploit CLM vulnerabilities?
   - Can liquidity be deployed without TWAP checks?
   - Can owner manipulate TWAP parameters?
   - Do rounding errors accumulate stuck tokens?
   - Are old approvals revoked on router updates?
   - Can fees be changed retrospectively?
   - Verify no compensating protections exist
   - Downgrade severity if admin-only unless enables rug pull or sandwich attack

4. **Generate report**
   - Use deliverable template below
   - Include sandwich attack analysis and PoC
   - Rank by severity

## Core Vulnerability Patterns

See `reference.md` for full checklist. Key patterns:

1. Forced unfavorable liquidity deployment → missing TWAP checks in some functions allow sandwich attacks
2. Owner rug-pull via TWAP parameters → setting ineffective maxDeviation/twapInterval disables protection
3. Tokens permanently stuck → rounding errors accumulate tokens that can never be withdrawn
4. Stale token approvals → router updates don't revoke previous approvals
5. Retrospective fee application → updated fees apply to previously earned rewards

**Code examples:** See `example.md`

## Severity Criteria

**Critical:** Missing TWAP checks in liquidity deployment enabling sandwich attacks, owner can disable TWAP protection to rug users, **MUST be exploitable by non-privileged actors**
**High:** Tokens permanently stuck from rounding errors, stale approvals allowing old router to drain funds, **MUST be exploitable by non-privileged actors**
**Medium:** Retrospective fee application on earned rewards, suboptimal TWAP parameters, **admin-only rebalance configuration issues with cascading user MEV exposure**
**Low:** Gas inefficiencies in rebalancing, missing events for parameter changes, **admin-only parameter issues without immediate user impact**

**IMPORTANT:** Admin-only CLM functions (onlyOwner, onlyAdmin, onlyGovernance) are **MEDIUM or LOW severity** unless:
- Admin can disable TWAP protection to sandwich user deposits/withdrawals
- Missing validation in TWAP parameter setters enables owner rug pull
- Admin router updates leave stale approvals allowing fund drainage

## False Positives - Do NOT Flag

- Protocols with trusted admins explicitly documented
- Test environments with simplified TWAP logic
- Functions only callable by trusted contracts
- Intentional dust accumulation with withdrawal mechanism
- Manual approval management by governance
- **Admin-only rebalance functions** (onlyOwner, onlyAdmin) with TWAP checks and documented trusted operator
- Governance-controlled TWAP parameter updates with bounds validation (minDeviation, maxDeviation)
- Admin router updates that properly revoke old approvals before setting new ones

## Deliverable Format

**MANDATORY:** Before deliverable, verify each `checklist.md` item against codebase. Flag violations as findings.

Use template: `templates/report-template.md`

Each finding includes: severity, pattern #, file/lines, description, vulnerable code, sandwich attack analysis, PoC, remediation.

## Key Principles

- **TWAP everywhere** - all liquidity deployment must check TWAP
- **Parameter bounds** - maxDeviation/twapInterval must have min/max limits
- **Zero dust** - no token accumulation in contracts
- **Approval hygiene** - revoke old approvals before new ones
- **Fee immutability** - fees cannot change for earned rewards

## Output Guidelines

**DO:**
- Reference specific lines and functions
- Provide sandwich attack scenarios
- Show PoCs with price manipulation
- Calculate rounding error accumulation
- Identify all liquidity deployment paths

**DON'T:**
- Report missing TWAP in view functions (not exploitable)
- Flag intentional dust with withdrawal mechanism
- Ignore parameter validation (critical for protection)
- Miss indirect liquidity deployment paths

## checklist.md

# Concentrated Liquidity Manager Security Checklist

Verify each item before finalizing audit report:

- [ ] **TWAP checks everywhere:** ALL functions deploying liquidity validate current price against TWAP
- [ ] **TWAP parameter bounds:** maxDeviation and twapInterval have enforced min/max limits
- [ ] **No token accumulation:** No tokens stuck in contract beyond active positions, or sweep function exists
- [ ] **Approval revocation:** Old approvals revoked before router updates
- [ ] **Fee immutability:** Fees collected before fee structure changes

## example.md

# Concentrated Liquidity Manager Vulnerability Examples

## Pattern #1: Forced Unfavorable Liquidity Deployment

### VULNERABLE
```solidity
contract VulnerableCLM {
    uint256 public maxDeviation = 200; // 2%
    uint32 public twapInterval = 1800; // 30 min

    // ISSUE: rebalance() has TWAP check
    function rebalance() external {
        _checkTWAP();
        _deployLiquidity();
    }

    // ISSUE: deposit() missing TWAP check!
    function deposit(uint256 amount0, uint256 amount1) external {
        // No TWAP validation
        // MEV bot can sandwich this function

        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);

        // Deploys liquidity at current (manipulated) price
        _deployLiquidity();
    }

    // Attack: Sandwich deposit() to force unfavorable liquidity deployment
}
```

### FIXED
```solidity
contract FixedCLM {
    uint256 public maxDeviation = 200; // 2%
    uint32 public twapInterval = 1800; // 30 min

    function rebalance() external {
        _checkTWAP();
        _deployLiquidity();
    }

    function deposit(uint256 amount0, uint256 amount1) external {
        // TWAP check in ALL liquidity deployment functions
        _checkTWAP();

        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);

        _deployLiquidity();
    }

    function _checkTWAP() internal view {
        (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
        uint160 sqrtPriceTWAP = _getSqrtTWAP();

        uint256 priceDiff = sqrtPriceX96 > sqrtPriceTWAP
            ? sqrtPriceX96 - sqrtPriceTWAP
            : sqrtPriceTWAP - sqrtPriceX96;

        require(
            priceDiff * 10000 / sqrtPriceTWAP <= maxDeviation,
            "Price deviation too high"
        );
    }
}
```

## Pattern #2: Owner Rug-Pull via TWAP Parameters

### VULNERABLE
```solidity
contract VulnerableTWAPParams {
    uint256 public maxDeviation; // No bounds
    uint32 public twapInterval; // No bounds

    // ISSUE: Owner can disable TWAP protection
    function setTWAPParams(uint256 _maxDeviation, uint32 _twapInterval) external onlyOwner {
        maxDeviation = _maxDeviation; // Can set to 10000 (100%)
        twapInterval = _twapInterval; // Can set to 1 (1 second)
    }

    // With 100% deviation or 1-second TWAP, protection is useless
    function _checkTWAP() internal view {
        (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
        uint160 sqrtPriceTWAP = _getSqrtTWAP(); // 1-second TWAP ≈ spot price

        uint256 priceDiff = sqrtPriceX96 > sqrtPriceTWAP
            ? sqrtPriceX96 - sqrtPriceTWAP
            : sqrtPriceTWAP - sqrtPriceX96;

        // 100% deviation allows any price manipulation
        require(priceDiff * 10000 / sqrtPriceTWAP <= maxDeviation, "Too high");
    }

    // Attack:
    // 1. Owner sets maxDeviation = 10000 (100%)
    // 2. Owner coordinates with MEV bot
    // 3. MEV bot sandwiches rebalance()
    // 4. Protocol deploys at terrible price
    // 5. Owner and MEV bot split profit
}
```

### FIXED
```solidity
contract FixedTWAPParams {
    uint256 public maxDeviation;
    uint32 public twapInterval;

    // Enforce reasonable bounds
    uint256 public constant MIN_MAX_DEVIATION = 10; // 0.1%
    uint256 public constant MAX_MAX_DEVIATION = 500; // 5%
    uint32 public constant MIN_TWAP_INTERVAL = 300; // 5 minutes
    uint32 public constant MAX_TWAP_INTERVAL = 3600; // 1 hour

    function setTWAPParams(uint256 _maxDeviation, uint32 _twapInterval) external onlyOwner {
        require(
            _maxDeviation >= MIN_MAX_DEVIATION && _maxDeviation <= MAX_MAX_DEVIATION,
            "Deviation out of bounds"
        );
        require(
            _twapInterval >= MIN_TWAP_INTERVAL && _twapInterval <= MAX_TWAP_INTERVAL,
            "Interval out of bounds"
        );

        maxDeviation = _maxDeviation;
        twapInterval = _twapInterval;
    }

    // Owner cannot disable protection
    // Users can verify parameters are reasonable
}
```

## Pattern #3: Tokens Permanently Stuck

### VULNERABLE
```solidity
contract VulnerableStuckTokens {
    uint256 public tokenId; // Current Uniswap V3 position

    function rebalance() external {
        // Burn old position
        (uint256 amount0, uint256 amount1) = positionManager.decreaseLiquidity(
            INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: tokenId,
                liquidity: liquidity,
                amount0Min: 0,
                amount1Min: 0,
                deadline: block.timestamp
            })
        );

        // Collect tokens
        positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: tokenId,
                recipient: address(this),
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );

        // ISSUE: Rounding errors in Uniswap V3
        // Some dust tokens remain uncollected
        // These accumulate in contract over time

        // Mint new position
        (tokenId, , , ) = positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: address(token0),
                token1: address(token1),
                fee: 3000,
                tickLower: newTickLower,
                tickUpper: newTickUpper,
                amount0Desired: token0.balanceOf(address(this)),
                amount1Desired: token1.balanceOf(address(this)),
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(this),
                deadline: block.timestamp
            })
        );

        // After 1000 rebalances, stuck tokens can be significant
    }

    // No function to withdraw stuck tokens
    // Tokens permanently locked in contract
}
```

### FIXED
```solidity
contract FixedStuckTokens {
    uint256 public tokenId;

    function rebalance() external {
        // ... rebalancing logic ...
    }

    // Add sweep function to rescue stuck tokens
    function sweepTokens(address token, address to) external onlyOwner {
        // Calculate tokens that should be in contract
        uint256 expectedBalance = _getExpectedBalance(token);

        // Only allow sweeping excess (stuck from rounding)
        uint256 actualBalance = IERC20(token).balanceOf(address(this));
        require(actualBalance > expectedBalance, "No excess tokens");

        uint256 excess = actualBalance - expectedBalance;
        IERC20(token).transfer(to, excess);
    }

    function _getExpectedBalance(address token) internal view returns (uint256) {
        // Calculate tokens that should be in positions
        // Returns amount locked in Uniswap V3 position
        (,,,,,,, uint128 liquidity,,,,) = positionManager.positions(tokenId);

        // Convert liquidity to token amounts
        // Account for tokens that should be in contract
        // ...

        return expectedAmount;
    }
}
```

## Pattern #4: Stale Token Approvals

### VULNERABLE
```solidity
contract VulnerableStaleApprovals {
    INonfungiblePositionManager public positionManager;

    constructor(address _positionManager) {
        positionManager = INonfungiblePositionManager(_positionManager);

        // Initial approvals
        token0.approve(address(positionManager), type(uint256).max);
        token1.approve(address(positionManager), type(uint256).max);
    }

    // ISSUE: Updating router doesn't revoke old approvals
    function setPositionManager(address _newManager) external onlyOwner {
        positionManager = INonfungiblePositionManager(_newManager);

        // Approve new manager
        token0.approve(_newManager, type(uint256).max);
        token1.approve(_newManager, type(uint256).max);

        // Old manager still has approval!
        // If old manager compromised, can drain all tokens
    }
}
```

### FIXED
```solidity
contract FixedStaleApprovals {
    INonfungiblePositionManager public positionManager;

    constructor(address _positionManager) {
        positionManager = INonfungiblePositionManager(_positionManager);

        token0.approve(address(positionManager), type(uint256).max);
        token1.approve(address(positionManager), type(uint256).max);
    }

    function setPositionManager(address _newManager) external onlyOwner {
        address oldManager = address(positionManager);

        // Revoke old approvals FIRST
        token0.approve(oldManager, 0);
        token1.approve(oldManager, 0);

        // Update manager
        positionManager = INonfungiblePositionManager(_newManager);

        // Approve new manager
        token0.approve(_newManager, type(uint256).max);
        token1.approve(_newManager, type(uint256).max);

        emit PositionManagerUpdated(oldManager, _newManager);
    }
}
```

## Pattern #5: Retrospective Fee Application

### VULNERABLE
```solidity
contract VulnerableRetrospectiveFees {
    uint256 public protocolFeePercent = 1000; // 10%
    mapping(address => uint256) public pendingFees;

    function collectFees() external {
        // Collect fees from Uniswap V3 position
        (uint256 amount0, uint256 amount1) = positionManager.collect(...);

        // ISSUE: Protocol fee applied when collected, not when earned
        uint256 protocolAmount0 = amount0 * protocolFeePercent / 10000;
        uint256 protocolAmount1 = amount1 * protocolFeePercent / 10000;

        protocolFees0 += protocolAmount0;
        protocolFees1 += protocolAmount1;

        userFees0 += amount0 - protocolAmount0;
        userFees1 += amount1 - protocolAmount1;
    }

    // Owner can increase fee before collection
    function setProtocolFee(uint256 newFee) external onlyOwner {
        protocolFeePercent = newFee; // Can set to 50% (5000)
        // Applies to already earned but uncollected fees!
    }

    // Attack:
    // 1. Fees earned over 30 days at 10% protocol fee
    // 2. Large amount of uncollected fees
    // 3. Owner sets fee to 50%
    // 4. collectFees() takes 50% of 30 days of fees
    // 5. Users lose 40% more than expected
}
```

### FIXED
```solidity
contract FixedRetrospectiveFees {
    uint256 public protocolFeePercent = 1000; // 10%

    function collectFees() external {
        (uint256 amount0, uint256 amount1) = positionManager.collect(...);

        uint256 protocolAmount0 = amount0 * protocolFeePercent / 10000;
        uint256 protocolAmount1 = amount1 * protocolFeePercent / 10000;

        protocolFees0 += protocolAmount0;
        protocolFees1 += protocolAmount1;

        userFees0 += amount0 - protocolAmount0;
        userFees1 += amount1 - protocolAmount1;
    }

    function setProtocolFee(uint256 newFee) external onlyOwner {
        // Collect existing fees with old rate FIRST
        collectFees();

        // Then update rate
        protocolFeePercent = newFee;

        // New rate only applies to future fees
    }
}
```

## Advanced Example: Complete CLM Attack

### VULNERABLE
```solidity
contract CompleteCLMVulnerable {
    uint256 public maxDeviation = 10000; // 100% - ineffective
    uint32 public twapInterval = 1; // 1 second - ineffective

    function deposit(uint256 amount0, uint256 amount1) external {
        // Missing TWAP check
        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);
        _mintPosition();
    }
}

contract AttackCLM {
    CompleteCLMVulnerable clm;
    IUniswapV3Pool pool;

    function attack() external {
        // 1. Flash loan 1000 ETH
        // 2. Swap 500 ETH -> USDC to move price 10%
        pool.swap(...);

        // 3. Deposit into CLM at manipulated price
        clm.deposit(100 ether, 300000 * 1e6);
        // CLM deploys liquidity at manipulated 10% worse price

        // 4. Swap back USDC -> ETH
        pool.swap(...);

        // 5. Repay flash loan
        // 6. Profit from CLM's impermanent loss
    }
}
```

### FIXED
```solidity
contract CompleteCLMFixed {
    uint256 public maxDeviation = 200; // 2% max
    uint32 public twapInterval = 1800; // 30 minutes

    uint256 public constant MIN_MAX_DEVIATION = 10;
    uint256 public constant MAX_MAX_DEVIATION = 500;

    function deposit(uint256 amount0, uint256 amount1) external {
        _checkTWAP(); // Protection enabled

        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);
        _mintPosition();
    }

    function _checkTWAP() internal view {
        (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
        uint160 sqrtPriceTWAP = _getSqrtTWAP();

        uint256 priceDiff = sqrtPriceX96 > sqrtPriceTWAP
            ? sqrtPriceX96 - sqrtPriceTWAP
            : sqrtPriceTWAP - sqrtPriceX96;

        require(
            priceDiff * 10000 / sqrtPriceTWAP <= maxDeviation,
            "Price manipulation detected"
        );
    }

    function setTWAPParams(uint256 _max, uint32 _interval) external onlyOwner {
        require(_max >= MIN_MAX_DEVIATION && _max <= MAX_MAX_DEVIATION, "Out of bounds");
        maxDeviation = _max;
        twapInterval = _interval;
    }
}
```

## Summary: Key Protections

1. **TWAP everywhere:** Check TWAP in ALL liquidity deployment functions
2. **Parameter bounds:** Enforce min/max on maxDeviation (0.1%-5%) and twapInterval (5min-1hr)
3. **Sweep function:** Allow rescuing stuck tokens from rounding errors
4. **Revoke approvals:** Zero old approvals before setting new router
5. **Collect before update:** Collect fees before changing fee structure

## TWAP Validation Template

```solidity
function _checkTWAP() internal view {
    (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();

    // Get TWAP over configured interval
    uint32[] memory secondsAgos = new uint32[](2);
    secondsAgos[0] = twapInterval;
    secondsAgos[1] = 0;

    (int56[] memory tickCumulatives, ) = pool.observe(secondsAgos);
    int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
    int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(twapInterval)));

    uint160 sqrtPriceTWAP = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);

    // Check deviation
    uint256 priceDiff = sqrtPriceX96 > sqrtPriceTWAP
        ? sqrtPriceX96 - sqrtPriceTWAP
        : sqrtPriceTWAP - sqrtPriceX96;

    require(
        priceDiff * 10000 / sqrtPriceTWAP <= maxDeviation,
        "Price deviation exceeded"
    );
}
```

## reference.md

# Concentrated Liquidity Manager Vulnerability Patterns

## Pattern #1: Forced Unfavorable Liquidity Deployment
**Risk:** Some functions deploy liquidity without TWAP checks, allowing MEV bots to sandwich attack and force protocol to deploy at manipulated prices
**Detection:** Verify ALL functions that mint positions or add liquidity validate current price against TWAP
**Impact:** Protocol loses funds to sandwich attacks, liquidity deployed at unfavorable prices causing immediate impermanent loss

## Pattern #2: Owner Rug-Pull via TWAP Parameters
**Risk:** Owner can set ineffective maxDeviation (e.g., 100%) or twapInterval (e.g., 1 second) that disable TWAP protection
**Detection:** Check if TWAP parameters have minimum/maximum bounds enforced
**Impact:** Owner disables protection and coordinates with MEV bot to sandwich attack protocol's liquidity deployments

## Pattern #3: Tokens Permanently Stuck
**Risk:** Rounding errors from Uniswap V3 position management accumulate tokens in contract that can never be withdrawn
**Detection:** Check if token balances can grow beyond what's in positions, and if sweep/rescue function exists
**Impact:** Protocol loses accumulated tokens permanently, can be significant over time

## Pattern #4: Stale Token Approvals
**Risk:** Updating router address doesn't revoke approvals to old router, allowing old compromised router to drain funds
**Detection:** Verify router updates revoke old approvals before setting new router
**Impact:** If old router compromised, attacker can drain all approved tokens

## Pattern #5: Retrospective Fee Application
**Risk:** Changing protocol fee percentage applies to already earned but uncollected rewards
**Detection:** Check if fees collected before fee structure updates
**Impact:** Users lose more fees than expected, protocol takes larger share of earned rewards retroactively

## templates

```

```

## templates/report-template.md

# Concentrated Liquidity Manager Security Audit Report

## Executive Summary

**Contract:** [Contract Name]
**Audit Date:** [Date]
**Auditor:** [Name/Team]

**Findings Overview:**
- Critical: X
- High: X
- Medium: X
- Low: X

## Findings

---

### [SEVERITY] Finding #X: [Vulnerability Title]

**Pattern:** #X - [Pattern Name from reference.md]

**Location:** `[contract_name.sol:line_numbers]`

**Description:**

[Detailed explanation of the CLM vulnerability]

**Vulnerable Code:**

```solidity
function deposit() external {
    // Missing TWAP check
    _deployLiquidity();
}
```

**Sandwich Attack Analysis:**

[Analyze exploitation scenario:]
- **Attack vector:** [Price manipulation via flash loan/large swap]
- **Manipulation magnitude:** [% price moved]
- **Protocol impact:** [Impermanent loss from unfavorable deployment]
- **Attacker profit:** [Calculation of MEV extraction]

**Proof of Concept:**

```solidity
contract SandwichCLM {
    function attack() external {
        // 1. Flash loan large amount
        // 2. Swap to manipulate pool price
        // 3. Call vulnerable CLM function
        // 4. CLM deploys at manipulated price
        // 5. Swap back
        // 6. Repay flash loan
        // 7. Profit from CLM's impermanent loss
    }
}
```

**Attack Flow:**
1. [Initial state - pool price, CLM holdings]
2. [Attacker swaps X tokens to move price Y%]
3. [CLM function called, deploys liquidity at manipulated price]
4. [Attacker swaps back, price returns to normal]
5. [CLM position now has immediate impermanent loss]
6. [Attacker profit: flash loan fee vs IL extracted]

**Impact Analysis:**

**Direct Impact:**
- [Immediate IL - e.g., "5% loss on $X liquidity deployed"]
- [User fund impact - e.g., "All depositors share loss"]

**Systemic Impact:**
- [Repeated exploitation - e.g., "Every rebalance/deposit sandwichable"]
- [Economic damage - e.g., "$Y cumulative losses possible"]

**Affected Functions:**
- [List all functions deploying liquidity without TWAP]

**Remediation:**

```solidity
function deposit() external {
    // Add TWAP check before liquidity deployment
    _checkTWAP();
    _deployLiquidity();
}

function _checkTWAP() internal view {
    (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
    uint160 sqrtPriceTWAP = _getSqrtTWAP();

    uint256 priceDiff = sqrtPriceX96 > sqrtPriceTWAP
        ? sqrtPriceX96 - sqrtPriceTWAP
        : sqrtPriceTWAP - sqrtPriceX96;

    require(
        priceDiff * 10000 / sqrtPriceTWAP <= maxDeviation,
        "Price deviation too high"
    );
}
```

**Recommendations:**
1. [Primary fix - e.g., "Add TWAP check to ALL liquidity deployment functions"]
2. [Parameter bounds - e.g., "Enforce maxDeviation between 0.1%-5%"]
3. [Defense in depth - e.g., "Add time delay between deposit and deployment"]

**Gas Impact:** [Estimated additional gas cost]
- TWAP check: ~20,000 gas per call

---

### [SEVERITY] Finding #X: [Next Vulnerability]

[Repeat above structure for each finding]

---

## Severity Definitions

**Critical:** Missing TWAP checks enabling sandwich attacks on liquidity deployment, owner can disable TWAP protection via parameter manipulation.

**High:** Tokens permanently stuck from rounding errors with no rescue mechanism, stale approvals allowing compromised router to drain funds, significant loss from retrospective fee application.

**Medium:** Suboptimal TWAP parameters reducing protection effectiveness, missing events for critical parameter changes.

**Low:** Gas inefficiencies in rebalancing logic, missing view functions for transparency.

## Recommendations Summary

### Immediate Actions (Critical/High)
1. [List critical fixes]
   - Example: "Add TWAP validation to deposit() and mint() functions"
   - Example: "Enforce bounds on maxDeviation (10-500 bps) and twapInterval (300-3600s)"
   - Example: "Implement sweepTokens() function for stuck tokens"

### Short-term Improvements (Medium)
1. [List medium-priority enhancements]
   - Example: "Collect fees before fee structure updates"
   - Example: "Add events for all parameter changes"

### Long-term Enhancements (Low)
1. [List optimization opportunities]
   - Example: "Optimize gas usage in rebalancing"

## Checklist Results

Based on `checklist.md`:

- [x] **TWAP checks everywhere:** All deployment functions validate TWAP ✓/✗
- [x] **TWAP parameter bounds:** maxDeviation/twapInterval bounded ✓/✗
- [x] **No token accumulation:** No stuck tokens or sweep exists ✓/✗
- [x] **Approval revocation:** Old approvals revoked on updates ✓/✗
- [x] **Fee immutability:** Fees collected before structure changes ✓/✗

## Function Analysis

### Liquidity Deployment Functions

| Function | TWAP Check? | Risk |
|----------|-------------|------|
| rebalance() | Yes | Low |
| deposit() | No | CRITICAL |
| mint() | No | CRITICAL |
| compound() | Yes | Low |

### TWAP Parameter Configuration

- **Current maxDeviation:** [X bps]
- **Recommended range:** 10-500 bps (0.1%-5%)
- **Current twapInterval:** [X seconds]
- **Recommended range:** 300-3600 seconds (5min-1hr)

### Token Accumulation Analysis

```
Expected balance (in positions): X tokens
Actual balance (in contract): Y tokens
Stuck tokens: Y - X tokens
Estimated value: $Z
```

## Sandwich Attack Economics

### Example Scenario

**Initial State:**
- Pool: 1000 ETH / 3M USDC
- Price: 1 ETH = 3000 USDC
- CLM deploying: 100 ETH

**Attack:**
1. Attacker flash borrows 500 ETH
2. Swaps 250 ETH → USDC (price moves to 3150 USDC/ETH, +5%)
3. CLM deploys 100 ETH at manipulated 3150 price
4. Attacker swaps back USDC → ETH (price returns to 3000)
5. CLM position has 5% impermanent loss immediately

**Profit Calculation:**
- CLM impermanent loss: ~2.5 ETH (~$7,500)
- Flash loan fee: ~0.09% of 500 ETH = 0.45 ETH (~$1,350)
- Net attacker profit: ~2 ETH (~$6,000)
- Protocol/users loss: ~2.5 ETH (~$7,500)

## Testing Recommendations

### Unit Tests
- [ ] TWAP check enforcement in all deployment functions
- [ ] TWAP parameter bounds validation
- [ ] Sweep function for stuck tokens
- [ ] Approval revocation on router updates
- [ ] Fee collection before structure changes

### Integration Tests
- [ ] End-to-end sandwich attack simulation
- [ ] Parameter manipulation scenarios
- [ ] Multi-rebalance token accumulation
- [ ] Router upgrade with approval management

### Scenario Tests
- [ ] High volatility with frequent rebalances
- [ ] Owner attempts to set ineffective TWAP params
- [ ] Long-term dust accumulation over 1000 rebalances
- [ ] Router compromise with stale approvals

## Appendix

### TWAP Implementation Reference

```solidity
function _getSqrtTWAP() internal view returns (uint160 sqrtPriceX96) {
    uint32[] memory secondsAgos = new uint32[](2);
    secondsAgos[0] = twapInterval;
    secondsAgos[1] = 0;

    (int56[] memory tickCumulatives, ) = pool.observe(secondsAgos);

    int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
    int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(twapInterval)));

    sqrtPriceX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);
}
```

### Recommended TWAP Parameters by Pool Type

| Pool Type | maxDeviation | twapInterval | Rationale |
|-----------|-------------|--------------|-----------|
| Stablecoin/Stablecoin | 10-50 bps | 300-600s | Low volatility |
| ETH/Stablecoin | 100-200 bps | 900-1800s | Medium volatility |
| Alt/ETH | 200-500 bps | 1800-3600s | High volatility |

### Impermanent Loss Formula

For price change of x%:
```
IL = 2 * sqrt(1 + x) / (1 + x) - 1
```

Example:
- 5% price change: ~0.5% IL
- 10% price change: ~2% IL
- 25% price change: ~6% IL

### Router Upgrade Checklist

1. [ ] Collect all pending fees
2. [ ] Burn current position
3. [ ] Collect all tokens from position
4. [ ] Revoke approvals to old router
5. [ ] Set new router address
6. [ ] Approve new router
7. [ ] Mint new position
8. [ ] Emit RouterUpdated event

### Stuck Token Calculation

```solidity
function calculateStuckTokens() public view returns (uint256 token0Stuck, uint256 token1Stuck) {
    // Actual balances
    uint256 balance0 = token0.balanceOf(address(this));
    uint256 balance1 = token1.balanceOf(address(this));

    // Expected balances (in position)
    (uint256 expected0, uint256 expected1) = _getPositionAmounts(tokenId);

    // Stuck = actual - expected
    token0Stuck = balance0 > expected0 ? balance0 - expected0 : 0;
    token1Stuck = balance1 > expected1 ? balance1 - expected1 : 0;
}
```

### Fee Collection Before Update Pattern

```solidity
function setProtocolFee(uint256 newFee) external onlyOwner {
    // MUST collect existing fees with old rate first
    _collectAllFees();

    // Then update
    protocolFeePercent = newFee;

    emit ProtocolFeeUpdated(newFee);
}
```

