# Oracle

The Oracle system provides two types of data to the Cap protocol: **asset prices** (USD values for backing assets, collateral tokens, and Cap-native tokens) and **interest rates** (borrow rates for vault utilization and per-Operator restaker rates). Both systems share the same adapter architecture — a central contract stores a payload per asset, and delegates the actual calculation to a stateless library via `staticcall`. This makes the oracle fully composable: any data source can be integrated by deploying a new adapter library.

## Mechanics

### Adapter Architecture

Each asset has an `OracleData` struct stored on-chain containing two fields:

* `adapter` — the address of the library or contract that computes the value
* `payload` — ABI-encoded calldata passed to the adapter

When a price or rate is requested, the Oracle does a low-level `staticcall` to `adapter(payload)`. Errors in the adapter are silently swallowed — a failed call returns zero rather than reverting. This means any data source failure falls through to the backup rather than halting the protocol.

### Price Oracle: Primary and Backup

Each asset has two `OracleData` slots — primary and backup. `getPrice` attempts the primary first:

1. Call `primary.adapter` with `primary.payload`.
2. If the returned price is zero **or** the timestamp is older than `staleness[asset]`, fall through.
3. Call `backup.adapter` with `backup.payload`.
4. If backup also fails or is stale, revert with `PriceError`.

All prices are normalized to **8 decimals** (USD). Adapters are responsible for decimal conversion.

### Rate Oracle: Market and Utilization

Rates drive two separate interest streams:

* **Market rate** — an external floor rate (e.g. Aave borrow rate). Used as a benchmark.
* **Utilization rate** — derived from the cUSD vault's time-weighted utilization (via VaultAdapter). This is the actual rate Operators pay on vault interest.

Both are fetched via the same adapter pattern, but rate adapters use `call` rather than `staticcall` because VaultAdapter writes state (it updates the utilization index snapshot).

Additionally, two admin-set rates are stored directly:

* `benchmarkRate[asset]` — a manual floor rate per asset (ray)
* `restakerRate[agent]` — per-Operator restaker interest rate (ray)

***

## Architecture

### PriceOracle

Abstract contract. Inherited by the central `Oracle` contract.

#### `getPrice(address _asset)`

```solidity
function getPrice(address _asset) external view returns (uint256 price, uint256 lastUpdated);
```

Returns the asset's USD price (8 decimals) and the timestamp of the last update. Tries the primary adapter first; falls back to the backup if the primary returns zero or a stale price. Reverts with `PriceError` if both fail.

***

#### Admin Functions

| Function                                               | Description                                      |
| ------------------------------------------------------ | ------------------------------------------------ |
| `setPriceOracleData(address _asset, OracleData)`       | Set primary price adapter + payload for an asset |
| `setPriceBackupOracleData(address _asset, OracleData)` | Set backup price adapter + payload for an asset  |
| `setStaleness(address _asset, uint256 _staleness)`     | Set max age (seconds) for a valid price          |

***

#### View Functions

| Function                         | Returns      | Description                                 |
| -------------------------------- | ------------ | ------------------------------------------- |
| `priceOracleData(address)`       | `OracleData` | Primary adapter + payload for the asset     |
| `priceBackupOracleData(address)` | `OracleData` | Backup adapter + payload for the asset      |
| `staleness(address)`             | `uint256`    | Max acceptable age in seconds for the price |

***

### RateOracle

Abstract contract. Inherited by the central `Oracle` contract.

#### `marketRate(address _asset)`

```solidity
function marketRate(address _asset) external returns (uint256 rate);
```

Returns the current market rate for `_asset` in ray format (`1e27 = 100%` annualised). Calls the configured market adapter (e.g. AaveAdapter). Returns zero if the adapter fails.

***

#### `utilizationRate(address _asset)`

```solidity
function utilizationRate(address _asset) external returns (uint256 rate);
```

Returns the utilization-based interest rate for `_asset` in ray. Calls the configured utilization adapter (VaultAdapter). This is the rate Operators pay on borrowed vault principal.

***

#### Admin Functions

| Function                                               | Description                                          |
| ------------------------------------------------------ | ---------------------------------------------------- |
| `setMarketOracleData(address _asset, OracleData)`      | Configure the market rate adapter + payload          |
| `setUtilizationOracleData(address _asset, OracleData)` | Configure the utilization rate adapter + payload     |
| `setBenchmarkRate(address _asset, uint256 _rate)`      | Set a manual benchmark rate floor (ray)              |
| `setRestakerRate(address _agent, uint256 _rate)`       | Set the restaker interest rate for an Operator (ray) |

***

#### View Functions

| Function                         | Returns         | Description                           |
| -------------------------------- | --------------- | ------------------------------------- |
| `benchmarkRate(address)`         | `uint256` (ray) | Manually set rate floor for the asset |
| `restakerRate(address)`          | `uint256` (ray) | Per-Operator restaker interest rate   |
| `marketOracleData(address)`      | `OracleData`    | Market adapter + payload              |
| `utilizationOracleData(address)` | `OracleData`    | Utilization adapter + payload         |

***

### Data Structures

#### `OracleData`

```solidity
struct OracleData {
    address adapter;
    bytes payload;
}
```

| Field     | Description                                                                    |
| --------- | ------------------------------------------------------------------------------ |
| `adapter` | Contract or library address called via `staticcall` (prices) or `call` (rates) |
| `payload` | ABI-encoded calldata passed directly to the adapter                            |

***

## Price Adapters

### ChainlinkAdapter

Reads a Chainlink price feed aggregator. Normalizes the returned value to 8 decimals regardless of the feed's native decimals.

**Payload**: `abi.encodeCall(ChainlinkAdapter.price, (_source))` where `_source` is the Chainlink aggregator address.

**Returns**: `(uint256 price, uint256 lastUpdated)` — price in 8 decimals, `lastUpdated` from `latestRoundData`.

***

### ChainlinkAdapterChained

Multiplies multiple Chainlink feeds together to derive a cross-rate. For example, to price an asset quoted in ETH: feed `A/ETH` × feed `ETH/USD`. The final price is normalized to 8 decimals; `lastUpdated` is the minimum across all feeds (most conservative).

**Payload**: `abi.encodeCall(ChainlinkAdapterChained.price, (_sources))` where `_sources` is an array of aggregator addresses.

***

### wstETHAdapter

Prices wstETH by reading the stETH/USD Chainlink feed and multiplying by the stETH-to-wstETH conversion ratio from the Lido contract (`getPooledEthByShares(1 ether)`).

**Payload**: `abi.encodeCall(wstETHAdapter.price, (_source, _stETH))` where `_source` is the stETH Chainlink aggregator and `_stETH` is the stETH token address.

***

### CapTokenAdapter

Prices a cUSD vault token (e.g. cUSD itself) as the weighted average of its backing assets:

```
price = Σ(totalSupplies[asset] × assetPrice / 10^assetDecimals) / capTokenSupply × 10^capDecimals
```

Calls back into the Oracle via `msg.sender` to get each underlying asset's price. `lastUpdated` is the minimum across all underlying prices. Returns `1e8` if supply is zero.

**Payload**: `abi.encodeCall(CapTokenAdapter.price, (_asset))` where `_asset` is the Cap vault token address.

***

### StakedCapAdapter

Prices stcUSD (staked cUSD) by reading the cUSD price and multiplying by the `convertToAssets` ratio of the ERC4626 staking vault:

```
price = capTokenPrice × convertToAssets(1e18) / 1e18
```

**Payload**: `abi.encodeCall(StakedCapAdapter.price, (_asset))` where `_asset` is the stcUSD address.

***

### FixedPriceOracle

A standalone contract (not a library) that always returns `1e8` (1 USD). Used for stablecoins or test deployments.

**Payload**: `abi.encodeCall(FixedPriceOracle.price, ())` — no parameters.

***

### UniswapV3Adapter

On-chain TWAP oracle using Uniswap V3 pool observations. Supports chained routes (e.g. `TOKEN → WETH → USDC`) to derive prices for any token with sufficient liquidity.

**Payload**: `abi.encode(tokens[], pools[], twapPeriods[])` where:

* `tokens` — ordered token route (length = `pools.length + 1`)
* `pools` — Uniswap V3 pool addresses for each hop
* `twapPeriods` — TWAP window in seconds per hop

The first token in the route must already have a registered price in the Oracle (used as the base price). The adapter validates the route via `validateData` before the payload is stored.

***

## Rate Adapters

### AaveAdapter

Reads Aave's `getReserveData` to fetch the current variable borrow rate for an asset. Used as a market rate benchmark.

**Payload**: `abi.encodeCall(AaveAdapter.rate, (_aaveDataProvider, _asset))`.

***

### VaultAdapter

Computes a utilization-based interest rate using Cap's own vault utilization history. This is the primary rate source for vault interest.

The adapter tracks a per-vault per-asset **utilization snapshot** (`index`, `lastUpdate`) and computes the time-weighted average utilization since the last call. The rate is determined by a two-slope model with a dynamic multiplier:

* **Below kink**: rate = `slope0 × utilization / kink × multiplier`. Multiplier decays when utilization is low.
* **Above kink**: rate = `(slope0 + slope1 × excess) × multiplier`. Multiplier grows when utilization is high.

The multiplier is bounded by `[minMultiplier, maxMultiplier]` and adjusts at speed `rate` (ray per second).

#### Admin Functions

| Function                                               | Description                                |
| ------------------------------------------------------ | ------------------------------------------ |
| `setSlopes(address _asset, SlopeData)`                 | Configure the two-slope curve for an asset |
| `setLimits(uint256 _max, uint256 _min, uint256 _rate)` | Set multiplier bounds and adjustment speed |

#### `SlopeData`

```solidity
struct SlopeData {
    uint256 slope0;  // ray — base rate at kink
    uint256 slope1;  // ray — additional rate above kink per unit of excess
    uint256 kink;    // ray — utilization ratio that separates the two slopes
}
```

***

## Usage Examples

### 1. Reading an asset price

```solidity
interface IOracle {
    function getPrice(address _asset) external view returns (uint256 price, uint256 lastUpdated);
}

address oracle = 0x...;
address usdc   = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

(uint256 price, uint256 updatedAt) = IOracle(oracle).getPrice(usdc);
// price = 1_0000_0000 (1.00 USD, 8 decimals)

uint256 usdValue = amount * price / 10 ** 6; // USDC has 6 decimals
```

***

### 2. Configuring a Chainlink price feed

```solidity
interface IOracle {
    function setPriceOracleData(address _asset, OracleData calldata) external;
    function setPriceBackupOracleData(address _asset, OracleData calldata) external;
    function setStaleness(address _asset, uint256 _staleness) external;
}

struct OracleData { address adapter; bytes payload; }

address oracle          = 0x...;
address chainlinkAdapter = 0x...;
address wbtcFeed        = 0x...; // Chainlink WBTC/USD aggregator
address wbtc            = 0x...; // WBTC token

// Primary: Chainlink WBTC/USD
IOracle(oracle).setPriceOracleData(wbtc, OracleData({
    adapter: chainlinkAdapter,
    payload: abi.encodeWithSignature("price(address)", wbtcFeed)
}));

// Allow up to 1 hour staleness
IOracle(oracle).setStaleness(wbtc, 3600);
```

***

### 3. Setting a restaker rate for an Operator

```solidity
interface IOracle {
    function setRestakerRate(address _agent, uint256 _rate) external;
    function restakerRate(address _agent) external view returns (uint256);
}

address oracle   = 0x...;
address operator = 0x...;

// Set 5% annual restaker rate (ray = 1e27)
IOracle(oracle).setRestakerRate(operator, 0.05e27);

// Verify
uint256 rate = IOracle(oracle).restakerRate(operator);
// rate = 5e25 (5% in ray)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cap.app/developers/contracts/oracle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
