# oracle-flashloan-analysis

Detects price oracle manipulation and flash loan attack vectors in DeFi smart contracts. Classifies oracle trust models (Chainlink, TWAP, spot price, custom), identifies stale price risks, circular price dependencies, and flash loan atomicity exploitation patterns. Use when auditing DeFi protocols that depend on price data, oracle integrations, lending protocols, DEXs, derivatives, or any contract where flash loans could manipulate state within a single transaction.

- **Kind:** skill
- **Source:** https://github.com/quillai-network/qs_skills
- **Page:** https://forefy.com/skills/c7d48c91-9be5-45c9-889b-512b7c139d28
- **API (JSON + files):** https://forefy.com/api/asr/c7d48c91-9be5-45c9-889b-512b7c139d28

---

## SKILL.md

---
name: oracle-flashloan-analysis
description: Detects price oracle manipulation and flash loan attack vectors in DeFi smart contracts. Classifies oracle trust models (Chainlink, TWAP, spot price, custom), identifies stale price risks, circular price dependencies, and flash loan atomicity exploitation patterns. Use when auditing DeFi protocols that depend on price data, oracle integrations, lending protocols, DEXs, derivatives, or any contract where flash loans could manipulate state within a single transaction.
---

# Oracle & Flash Loan Analysis

Detect vulnerabilities where **external price data can be manipulated** or **flash loans can exploit protocol logic** within a single transaction. These two attack vectors are often combined and represent the most common DeFi attack pattern.

## When to Use

- Auditing any DeFi protocol that reads external price data (lending, DEX, derivatives, yield aggregators)
- Reviewing Chainlink, Uniswap TWAP, Band Protocol, or custom oracle integrations
- Analyzing protocols that interact with or are accessible via flash loans
- Threat modeling for MEV, sandwich attacks, and price manipulation
- When a protocol uses `balanceOf()`, pool reserves, or spot prices for critical calculations

## When NOT to Use

- Contracts with no price dependencies or external data feeds
- Pure access control analysis (use semantic-guard-analysis)
- State-to-state invariant checking (use state-invariant-detection)

## Core Concept: The Oracle Trust Hierarchy

Not all price sources are equally secure. Oracle vulnerabilities stem from the gap between **assumed trust** and **actual manipulation resistance**.

```
Trust Level (highest to lowest):
┌─────────────────────────────────────────────┐
│ Level 5: Multi-oracle consensus + circuit    │
│          breakers + TWAP + staleness checks  │
├─────────────────────────────────────────────┤
│ Level 4: Chainlink with full validation      │
│          (staleness, sequencer, min answers)  │
├─────────────────────────────────────────────┤
│ Level 3: Uniswap V3 TWAP (long window)      │
│          Multi-block manipulation cost        │
├─────────────────────────────────────────────┤
│ Level 2: Uniswap V2 TWAP (short window)     │
│          or Chainlink WITHOUT staleness check │
├─────────────────────────────────────────────┤
│ Level 1: Spot price from single pool         │ ← Manipulable via flash loan
│          or balanceOf() for pricing           │
└─────────────────────────────────────────────┘
```

## The Four-Phase Detection Architecture

### Phase 1: Oracle Source Identification

Locate every point where the contract reads external price/value data.

**Search for these patterns:**

| Pattern | Oracle Type | Risk Level |
|---------|------------|------------|
| `latestRoundData()` | Chainlink | Medium (depends on validation) |
| `latestAnswer()` | Chainlink (deprecated) | HIGH (no round validation) |
| `observe()` / `consult()` | Uniswap TWAP | Medium (depends on window) |
| `getReserves()` | AMM spot price | **CRITICAL** (flash-loan manipulable) |
| `balanceOf(address(this))` | Self-balance | **CRITICAL** (donation attack) |
| `slot0()` / `sqrtPriceX96` | Uniswap V3 spot | **CRITICAL** (single-block manipulable) |
| Custom `getPrice()` | Unknown | Requires investigation |

**Build an Oracle Dependency Map:**

```
Contract: LendingPool
├── borrowLimit() → uses getCollateralPrice()
│   └── getCollateralPrice() → calls chainlinkOracle.latestRoundData()
├── liquidate() → uses getDebtPrice()
│   └── getDebtPrice() → calls uniswapPool.slot0() ← SPOT PRICE!
└── calculateInterest() → uses getUtilizationRate()
    └── getUtilizationRate() → reads internal state (safe)
```

### Phase 2: Oracle Validation Verification

For each oracle source, verify that proper safety checks are in place.

**Chainlink Validation Checklist:**

```solidity
// COMPLETE Chainlink integration
(uint80 roundId, int256 price, , uint256 updatedAt, uint80 answeredInRound) =
    priceFeed.latestRoundData();

require(price > 0, "Invalid price");                    // Check 1: Non-negative
require(updatedAt > 0, "Round not complete");            // Check 2: Round complete
require(answeredInRound >= roundId, "Stale price");      // Check 3: Not stale
require(block.timestamp - updatedAt < HEARTBEAT,         // Check 4: Fresh
        "Price too old");

// L2-specific
require(!sequencerFeed.isDown(), "Sequencer down");      // Check 5: L2 sequencer
require(block.timestamp - sequencerUptime > GRACE,       // Check 6: Grace period
        "Grace period");
```

**Missing Check Severity:**

| Missing Check | Severity | Impact |
|---------------|----------|--------|
| `price > 0` | HIGH | Zero/negative price → infinite borrowing or free liquidations |
| `updatedAt > 0` | MEDIUM | Incomplete round data used |
| `answeredInRound >= roundId` | HIGH | Stale price from previous round |
| Heartbeat/freshness | HIGH | Hours-old price during volatile markets |
| L2 sequencer check | HIGH | Stale price during L2 outage → unfair liquidations |
| Price deviation bounds | MEDIUM | Extreme outlier not filtered |

**TWAP Validation:**

```
Window length analysis:
  - < 10 minutes: HIGH RISK — manipulable with moderate capital
  - 10-30 minutes: MEDIUM RISK — expensive but feasible multi-block manipulation
  - 30+ minutes: LOWER RISK — requires sustained pool manipulation
  - Check: Is the TWAP window configurable? Can governance reduce it?
```

### Phase 3: Flash Loan Attack Surface Analysis

Identify operations that can be exploited via flash loan atomicity.

**Flash Loan Attack Model:**

```
Single Transaction:
  1. Borrow N tokens via flash loan (Aave, dYdX, Balancer)
  2. Manipulate price source (swap in pool, donate to contract)
  3. Exploit protocol at manipulated price (borrow, liquidate, swap)
  4. Reverse manipulation (swap back)
  5. Repay flash loan + fee
  6. Profit = exploited_value - flash_loan_fee - gas
```

**Detection Algorithm:**

```
For each function F that reads price/value data:
  1. Identify the price source S
  2. Can S be manipulated within a single transaction?
     - Spot price from AMM → YES (swap in same tx)
     - balanceOf(address(this)) → YES (donate tokens)
     - Chainlink feed → NO (off-chain updates)
     - TWAP → DEPENDS (short window = risky)
  3. What does F do with the price?
     - Determines borrowing limit → CRITICAL
     - Triggers liquidation → CRITICAL
     - Sets exchange rate → HIGH
     - Informational only → LOW
  4. Is the manipulation profitable?
     - value_extracted - (flash_loan_fee + slippage + gas) > 0 → EXPLOIT VIABLE
```

**Common Flash Loan Attack Patterns:**

| Pattern | Target | Method |
|---------|--------|--------|
| Oracle manipulation | Lending protocol | Flash swap in pool → inflate collateral price → over-borrow |
| Governance attack | DAO/voting | Flash borrow governance tokens → vote → execute → return |
| Liquidation manipulation | Lending protocol | Flash swap to crash price → liquidate at discount |
| Share price inflation | Vault/ERC4626 | Flash loan → donate to vault → inflate share price → front-run deposit |
| Arbitrage amplification | AMM/DEX | Flash loan amplifies existing price discrepancy |

### Phase 4: Circular Dependency Detection

Find cases where a protocol's pricing depends on its own state, creating exploitable feedback loops.

**Circular Dependency Pattern:**

```
Protocol A uses Token X price → from Pool P
Pool P contains Token X + Token Y
Protocol A issues Token X (or affects its supply)

→ CIRCULAR: Protocol A's actions change Token X supply
            → changes Pool P reserves
            → changes Token X price
            → changes Protocol A's valuations
```

**Detection:**

```
For each price oracle call in the contract:
  1. What token/asset is being priced?
  2. Does THIS contract mint, burn, or distribute that token?
  3. Does THIS contract add/remove liquidity from the pricing pool?
  4. Does any action in THIS contract affect the reserves of the pricing pool?

  If YES to any → CIRCULAR DEPENDENCY
  Severity: CRITICAL if the circular path can be exploited atomically
```

## Workflow

```
Task Progress:
- [ ] Step 1: Identify all oracle/price data sources in the contract
- [ ] Step 2: Classify each source by trust level (Chainlink, TWAP, spot, custom)
- [ ] Step 3: Verify validation checks for each oracle source
- [ ] Step 4: Map flash loan attack surfaces (which operations use manipulable prices?)
- [ ] Step 5: Detect circular price dependencies
- [ ] Step 6: Estimate manipulation cost vs profit (feasibility analysis)
- [ ] Step 7: Score findings and generate report
```

## Output Format

```markdown
## Oracle & Flash Loan Analysis Report

### Finding: [Title]

**Function:** `functionName()` at `Contract.sol:L42`
**Category:** [Oracle Manipulation | Stale Price | Flash Loan | Circular Dependency]
**Severity:** [CRITICAL | HIGH | MEDIUM]

**Oracle Source:** `[oracle contract/function]`
**Trust Level:** [1-5 from hierarchy]

**Vulnerability:**
[Description of how the price source can be manipulated or is insufficiently validated]

**Attack Scenario:**
1. Attacker obtains flash loan of [X tokens] from [source]
2. Swaps [amount] in [pool] to manipulate price of [token]
3. Calls `functionName()` which reads manipulated price
4. Extracts [value] from protocol at wrong price
5. Reverses manipulation and repays flash loan
6. Net profit: [amount]

**Missing Validations:**
- [ ] Price > 0 check
- [ ] Staleness check (heartbeat)
- [ ] Round completeness check
- [ ] L2 sequencer check
- [ ] Price deviation bounds

**Recommendation:**
[Specific fix — add TWAP, add Chainlink validation, implement circuit breaker]
```

## Quick Detection Checklist

- [ ] Does any function use `getReserves()`, `slot0()`, or `balanceOf()` for pricing? (Flash-loan manipulable)
- [ ] Does Chainlink integration check for `price > 0`, staleness, and round completeness?
- [ ] Is the TWAP window long enough to resist multi-block manipulation (> 30 min)?
- [ ] Does the protocol's own token appear in its pricing oracle's pool? (Circular dependency)
- [ ] Can any critical operation (borrow, liquidate, swap) be called in the same transaction as a flash loan?
- [ ] Are there price deviation circuit breakers for extreme moves?
- [ ] On L2: Is the sequencer uptime checked before using price data?

For oracle type details, see [{baseDir}/references/oracle-types.md]({baseDir}/references/oracle-types.md).
For flash loan attack patterns, see [{baseDir}/references/flash-loan-vectors.md]({baseDir}/references/flash-loan-vectors.md).

## Rationalizations to Reject

- "We use Chainlink, so it's safe" → Only if ALL validation checks are implemented; partial integration is common
- "Flash loans can't affect our protocol" → Any protocol using manipulable price sources is affected
- "The TWAP window is 10 minutes" → Multi-block manipulation is feasible for well-funded attackers
- "Our oracle is a trusted admin feed" → Admin key compromise → arbitrary price → instant drain
- "The pool is too large to manipulate" → Flash loans provide unlimited capital for single-transaction manipulation
- "We check if price is non-zero" → Non-zero is necessary but not sufficient; stale/manipulated non-zero prices are dangerous

## references

```

```

## references/flash-loan-vectors.md

# Flash Loan Attack Vectors — Detailed Reference

## Flash Loan Mechanics

Flash loans allow borrowing unlimited capital with zero collateral, provided the loan is repaid within the same transaction. This creates a new attack class: **atomicity exploitation**.

### Available Flash Loan Sources

| Source | Max Amount | Fee | Notes |
|--------|-----------|-----|-------|
| Aave V3 | Pool liquidity | 0.05-0.09% | Most popular, multi-asset |
| Balancer | Pool liquidity | 0% (flash swaps) | Free flash loans |
| dYdX | Pool liquidity | 0 (+ 2 wei) | Near-zero cost |
| Uniswap V2/V3 | Pool liquidity | 0.3% (flash swap) | Swap-based |
| Maker | DAI supply | 0% | DAI only |
| Euler | Pool liquidity | 0% | Multi-asset |

### Attack Template

```solidity
contract FlashLoanAttack {
    function execute() external {
        // Step 1: Borrow via flash loan
        aave.flashLoan(address(this), token, amount, "");
    }

    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool) {
        // Step 2: Manipulate (swap, donate, deposit)
        manipulatePrice();

        // Step 3: Exploit at manipulated price
        exploitProtocol();

        // Step 4: Reverse manipulation
        reverseManipulation();

        // Step 5: Repay flash loan + fee
        IERC20(asset).approve(address(aave), amount + premium);
        return true;
    }
}
```

---

## Attack Vector 1: Oracle Price Manipulation

### Pattern

```
1. Flash borrow large amount of Token A
2. Swap Token A → Token B in target pool
   → Token A price drops, Token B price rises
3. Exploit protocol that uses this pool for pricing
   → Borrow against inflated collateral, OR
   → Liquidate positions at artificial prices
4. Reverse swap (Token B → Token A)
5. Repay flash loan
```

### Real-World Example: Euler Finance ($197M, March 2023)

```
Attack flow:
1. Flash borrowed 30M DAI from Aave
2. Deposited into Euler, received eDAI
3. Used eDAI as collateral to borrow 10x leverage
4. Triggered self-liquidation at manipulated internal price
5. Donated to reserves to manipulate book values
6. Withdrew at inflated valuations
7. Repaid flash loan with profit

Root cause: Internal accounting used manipulable book values
```

### Real-World Example: Mango Markets ($114M, October 2022)

```
Attack flow:
1. Opened large perpetual position on MNGO-PERP
2. Used a second account to massively buy MNGO spot
3. MNGO spot price pumped → Mango's oracle reported inflated price
4. Unrealized PnL on perp position inflated
5. Used inflated account value as collateral to borrow all available assets
6. Let MNGO price crash back → account underwater but already drained

Root cause: Spot-price-based oracle used for collateral valuation
```

---

## Attack Vector 2: Governance Flash Loan Attack

### Pattern

```
1. Flash borrow governance tokens
2. Create proposal (or vote on existing)
3. Execute proposal in same transaction (if no timelock)
4. Proposal drains treasury or changes critical parameters
5. Return governance tokens
```

### Detection

```
For governance contracts:
  - Can proposal creation + voting + execution happen in one transaction?
  - Is there a timelock between proposal and execution?
  - Does voting weight snapshot BEFORE the vote transaction?
  - Can delegated votes be flash-borrowed?

If snapshot is at vote time (not block-1):
  → FLASH LOAN GOVERNANCE ATTACK possible
```

---

## Attack Vector 3: Vault Share Price Manipulation (ERC4626 Inflation)

### Pattern

```
1. Be the first (or early) depositor in a vault
2. Deposit minimal amount (1 wei) → receive 1 share
3. Donate large amount of underlying tokens directly to vault
4. Share price inflated: totalAssets = donated + 1 wei, totalShares = 1
5. Next depositor: deposit / inflated_share_price rounds to 0 shares
6. Attacker withdraws: gets their share + victim's deposit

With flash loans:
  - Flash borrow the donation amount
  - Donate → front-run victim deposit → withdraw → repay
  - All in one transaction
```

### Mitigation Detection

Check for:
- Virtual shares/assets offset (OpenZeppelin's approach)
- Minimum deposit amount enforcement
- Dead shares (first deposit goes to zero address)
- Internal asset tracking vs `balanceOf` for `totalAssets`

---

## Attack Vector 4: Liquidation Manipulation

### Pattern

```
1. Flash borrow collateral tokens of target user
2. Swap to crash collateral price in oracle pool
3. Target user's position now appears undercollateralized
4. Liquidate target at discount
5. Reverse price manipulation
6. Repay flash loan, keep liquidation bonus
```

### Detection

```
For lending/borrowing protocols:
  - Can collateral price be manipulated within one transaction?
  - Is there a liquidation delay or grace period?
  - Does the protocol use spot price or TWAP for liquidation triggers?
  - Is the liquidation bonus larger than manipulation cost?

If spot_price AND no_delay AND bonus > cost:
  → LIQUIDATION MANIPULATION viable
```

---

## Attack Vector 5: Circular Flash Loan (Amplification)

### Pattern

```
1. Flash borrow Token A
2. Deposit Token A into Protocol X → receive receipt token rA
3. Use rA as collateral in Protocol Y → borrow Token B
4. Swap Token B → Token A
5. Repeat (amplify position across protocols)
6. Eventually extract value from mispricing between protocols
7. Unwind and repay flash loan
```

### Detection

```
For protocols that accept other protocols' receipt tokens as collateral:
  → Map all cross-protocol deposit/borrow chains
  → Detect cycles: A → receipt → B → borrow → A
  → CIRCULAR FLASH LOAN possible if cycle exists
```

---

## Flash Loan Feasibility Assessment

For each potential flash loan attack, evaluate:

```
Profitability = Extracted_Value - (Flash_Fee + Gas + Slippage + Reversal_Loss)

Where:
  Flash_Fee: 0-0.09% of borrowed amount (often 0 via Balancer)
  Gas: ~500K-2M gas for complex attacks
  Slippage: Price impact of manipulation swaps
  Reversal_Loss: Cost of reversing manipulation (may not be exactly equal)

If Profitability > 0 → Attack is VIABLE
```

### Capital Requirements

```
Required capital ≈ Pool_Liquidity × Desired_Price_Impact

For a $10M liquidity pool and 50% price impact:
  Required capital ≈ $10M × 0.5 = $5M flash loan
  Available from: Aave ($Billions), Balancer (free), Uniswap
  → ALWAYS AVAILABLE for any pool that exists on-chain
```

---

## Combination Attack Patterns

| Primary Vector | Secondary Vector | Combined Effect |
|---------------|-----------------|-----------------|
| Oracle manipulation | Liquidation | Forced liquidation at artificial price |
| Oracle manipulation | Borrowing | Over-borrowing against inflated collateral |
| Share inflation | Front-running | Steal victim's deposit via rounding |
| Governance | Treasury drain | Flash-vote to extract protocol funds |
| Price crash | Short position | Profit from intentionally caused crash |
| Donation attack | Price inflation | Inflate balanceOf-based pricing |

## references/oracle-types.md

# Oracle Types — Classification and Trust Models

## Type 1: Chainlink Price Feeds

### How It Works

Off-chain oracle nodes aggregate prices from multiple exchanges and submit on-chain via decentralized oracle network. Updates occur on deviation threshold (e.g., 1% price change) or heartbeat interval.

### Interface

```solidity
interface AggregatorV3Interface {
    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
    function decimals() external view returns (uint8);
}
```

### Required Validations

```solidity
function getPrice() public view returns (uint256) {
    (
        uint80 roundId,
        int256 price,
        ,
        uint256 updatedAt,
        uint80 answeredInRound
    ) = priceFeed.latestRoundData();

    // CHECK 1: Price is positive
    require(price > 0, "Invalid price");

    // CHECK 2: Round is complete
    require(updatedAt > 0, "Incomplete round");

    // CHECK 3: Answer is from current round (not stale)
    require(answeredInRound >= roundId, "Stale price data");

    // CHECK 4: Price is recent (heartbeat check)
    require(block.timestamp - updatedAt <= MAX_PRICE_AGE, "Price too old");

    return uint256(price);
}
```

### Common Vulnerabilities

| Issue | Code Pattern | Severity |
|-------|-------------|----------|
| Using deprecated `latestAnswer()` | `oracle.latestAnswer()` | HIGH — no round validation |
| Missing staleness check | No `updatedAt` comparison | HIGH — hours-old price used |
| Missing negative price check | No `price > 0` | HIGH — negative price breaks math |
| Missing round completeness | No `answeredInRound >= roundId` | MEDIUM — incomplete round |
| Hardcoded feed address | No updateability | MEDIUM — can't rotate if feed deprecated |
| Wrong decimals assumption | Assuming 8 decimals always | MEDIUM — different feeds have different decimals |

### L2-Specific Concerns

On Arbitrum, Optimism, and other L2s, the sequencer can go down, causing stale prices:

```solidity
// Sequencer uptime feed check
(, int256 answer, , uint256 startedAt, ) = sequencerFeed.latestRoundData();
bool isSequencerUp = answer == 0;
require(isSequencerUp, "Sequencer is down");

uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD, "Grace period not over");
```

---

## Type 2: Uniswap V3 TWAP Oracle

### How It Works

Time-Weighted Average Price computed from accumulated tick values over a specified observation window. Resistant to single-block manipulation but vulnerable to sustained multi-block attacks.

### Interface

```solidity
// Uniswap V3 Pool
function observe(uint32[] calldata secondsAgos)
    external view returns (
        int56[] memory tickCumulatives,
        uint160[] memory secondsPerLiquidityCumulativeX128s
    );
```

### Price Calculation

```solidity
function getTWAP(uint32 twapInterval) public view returns (uint256) {
    uint32[] memory secondsAgos = new uint32[](2);
    secondsAgos[0] = twapInterval; // e.g., 1800 for 30-minute TWAP
    secondsAgos[1] = 0;

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

    int56 tickCumulativeDelta = tickCumulatives[1] - tickCumulatives[0];
    int24 arithmeticMeanTick = int24(tickCumulativeDelta / int56(int32(twapInterval)));

    return OracleLibrary.getQuoteAtTick(arithmeticMeanTick, baseAmount, baseToken, quoteToken);
}
```

### Manipulation Cost Analysis

```
Cost to manipulate TWAP ≈ (pool_liquidity × price_deviation × window_length) / block_time

Example:
  Pool liquidity: $10M
  Desired manipulation: 10% price change
  TWAP window: 30 minutes (150 blocks at 12s)
  Approximate cost: $10M × 10% × (1/150) ≈ $6,667 per block of manipulation
  Total for 30-min TWAP: ~$1M in capital lockup + trading losses

  For 5-minute TWAP: ~$200K → much more feasible
```

### Window Length Risk Assessment

| Window | Risk Level | Notes |
|--------|-----------|-------|
| < 5 min | CRITICAL | Easily manipulated with moderate capital |
| 5-15 min | HIGH | Feasible for well-funded attackers |
| 15-30 min | MEDIUM | Expensive but possible |
| 30-60 min | LOW | Very expensive sustained manipulation |
| > 60 min | VERY LOW | Impractical for most attackers |

---

## Type 3: AMM Spot Price (CRITICAL RISK)

### How It Works

Reads current reserves from an AMM pool and calculates price as `reserveA / reserveB`. This is the **most dangerous** oracle type because it can be manipulated within a single transaction via flash loans.

### Vulnerable Patterns

```solidity
// DANGEROUS: Spot price from Uniswap V2
function getPrice() public view returns (uint256) {
    (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
    return uint256(reserve1) * 1e18 / uint256(reserve0);
}

// DANGEROUS: Spot price from Uniswap V3
function getPrice() public view returns (uint256) {
    (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
    return (uint256(sqrtPriceX96) ** 2 * 1e18) >> 192;
}

// DANGEROUS: Price from contract balance
function getPrice() public view returns (uint256) {
    return address(this).balance * 1e18 / totalSupply;
}
```

### Why It's Dangerous

```
Normal state:
  Pool: 1000 ETH + 2,000,000 USDC → Price = 2000 USDC/ETH

Flash loan attack (single transaction):
  1. Flash borrow 10,000 ETH
  2. Swap 9,000 ETH into pool → Pool: 10,000 ETH + 220,000 USDC
  3. Spot price now: 22 USDC/ETH (90% drop!)
  4. Exploit protocol using crashed price
  5. Swap back, repay flash loan
  6. All in one transaction, one block
```

---

## Type 4: Custom / Admin-Controlled Oracle

### Pattern

```solidity
contract CustomOracle {
    address public admin;
    mapping(address => uint256) public prices;

    function setPrice(address token, uint256 price) external {
        require(msg.sender == admin, "Not admin");
        prices[token] = price;
    }

    function getPrice(address token) external view returns (uint256) {
        return prices[token];
    }
}
```

### Risks

| Risk | Severity | Description |
|------|----------|-------------|
| Admin key compromise | CRITICAL | Attacker sets arbitrary prices → instant protocol drain |
| No update mechanism | HIGH | Price goes stale if admin fails to update |
| Single point of failure | HIGH | One admin controls all pricing |
| No validation | MEDIUM | Admin can set zero or extreme prices |
| No timelock | MEDIUM | Price changes take effect immediately |

---

## Type 5: On-Chain Calculated Price (Self-Referencing)

### Pattern

```solidity
function getSharePrice() public view returns (uint256) {
    return totalAssets() / totalSupply();
}

function totalAssets() public view returns (uint256) {
    return asset.balanceOf(address(this)); // Donation-attackable!
}
```

### Risks

- **Donation attack**: Attacker sends tokens directly to contract, inflating `totalAssets` without minting shares
- **First depositor attack (ERC4626 inflation)**: Attacker manipulates share price for rounding exploitation
- **Self-referencing**: Protocol actions (deposit/withdraw) change the price used by the protocol itself

---

## Oracle Comparison Matrix

| Oracle Type | Flash Loan Resistant | Multi-Block Resistant | Decentralized | Cost |
|-------------|---------------------|----------------------|---------------|------|
| Chainlink (validated) | YES | YES | YES | Feed fees |
| Uniswap V3 TWAP (30m+) | YES | MOSTLY | YES | Gas only |
| Uniswap V2 TWAP (short) | YES | NO | YES | Gas only |
| AMM spot price | **NO** | **NO** | YES | Gas only |
| balanceOf() pricing | **NO** | **NO** | N/A | Gas only |
| Admin-controlled | YES (if honest) | YES (if honest) | **NO** | Manual |
| Multi-oracle consensus | YES | YES | YES | Highest |

