> For the complete documentation index, see [llms.txt](https://docs.curvance.com/app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.curvance.com/app/developer-docs/oracles/oracle-manager.md).

# Oracle Manager

### Overview

`OracleManager` is Curvance's protocol-level price hub. Market contracts call it to price underlying assets, cToken shares, collateral positions, and debt positions through a single interface.

The manager does not maintain price feeds itself. It routes each supported asset to one or more approved oracle adaptors, normalizes price results, applies cToken exchange-rate conversion when the queried asset is a Curvance cToken, and returns an error code that downstream contracts use as a safety signal.

Prices are returned with 18-decimal precision. A price can be denominated in USD or in the chain's native gas token, depending on the `inUSD` argument.

### Architecture

`OracleManager` is built around three registries:

| Registry             | Getter                        | Purpose                                                      |
| -------------------- | ----------------------------- | ------------------------------------------------------------ |
| Approved adaptors    | `isApprovedAdaptor(address)`  | Controls which adaptor contracts may be used by the manager. |
| Asset pricing config | `getPricingAdaptors(address)` | Lists the adaptor dependencies for an underlying asset.      |
| Curvance cTokens     | `cTokens(address)`            | Maps a supported cToken to its underlying asset.             |

The constructor stores the protocol `centralRegistry`, and every configuration function checks `centralRegistry.hasElevatedPermissions(msg.sender)` before changing oracle state.

```solidity
constructor(ICentralRegistry cr);
```

#### Supported Asset Model

An asset is supported when it has at least one configured pricing adaptor. A Curvance cToken is supported when it has been registered in `cTokens` and its underlying asset has at least one configured adaptor.

The manager supports up to two adaptor dependencies per asset. Current developer-facing integrations should treat normal pricing as single-source, with adaptor-level freshness and guard checks handled inside the configured adaptor. The two-adaptor code path exists for assets that are configured with two adaptor dependencies, where the manager can compare returned prices against configured deviation bounds.

Adaptors return `IOracleAdaptor.PricingResult`:

| Field      | Meaning                                          |
| ---------- | ------------------------------------------------ |
| `price`    | The adaptor's price value.                       |
| `inUSD`    | Whether `price` is USD-denominated.              |
| `hadError` | Whether the adaptor encountered a pricing issue. |

When an adaptor's denomination does not match the requested denomination, the manager converts through the configured price of the native gas token.

#### cToken Pricing

For registered cTokens, `getPrice()` first prices the underlying asset, then multiplies by the cToken exchange rate:

| Query                                    | Exchange-rate read                           | Rounding |
| ---------------------------------------- | -------------------------------------------- | -------- |
| `getPrice(cToken, ..., true)`            | `exchangeRate()`                             | Down     |
| `getPrice(cToken, ..., false)`           | `exchangeRate()`                             | Up       |
| `getPriceIsolatedPair()` collateral side | `exchangeRateUpdated()`                      | Down     |
| `getPricesForMarket()` collateral side   | `getSnapshotUpdated()` then `exchangeRate()` | Down     |

The market helper functions use updated cToken reads because they support liquidity checks that need current account and exchange-rate data.

### Price Query Flow

1. The caller requests a price through `getPrice(asset, inUSD, getLower)` or through a market helper.
2. The manager checks the optional `SEQUENCER_ORACLE()` uptime feed from the central registry. If no feed is configured, the check returns true.
3. If `asset` is a registered cToken, the manager replaces it with its underlying asset for adaptor pricing.
4. The manager loads the asset's adaptor list and reverts if no adaptor is configured.
5. With one adaptor, it calls `IOracleAdaptor.getPrice(asset, inUSD, getLower)`.
6. With two adaptors, it calls both adaptors, handles adaptor errors, checks deviation bounds, and returns either the lower or higher price based on `getLower`.
7. If the original query was for a cToken, the manager converts the underlying price into a cToken share price.
8. If the resulting price is zero, the manager escalates the result to `BAD_SOURCE`.

Use `getLower = true` when valuing collateral conservatively. Use `getLower = false` when valuing debt conservatively.

### Error Code Handling

`OracleManager` uses numeric error codes from `ConstantsLib`:

<table><thead><tr><th width="138">Code</th><th width="165">Name</th><th>Meaning</th></tr></thead><tbody><tr><td><code>0</code></td><td><code>NO_ERROR</code></td><td>The price was returned without an oracle-manager error signal.</td></tr><tr><td><code>1</code></td><td><code>CAUTION</code></td><td>One source failed in a two-adaptor configuration, or the two adaptor prices exceeded the configured caution bound.</td></tr><tr><td><code>2</code></td><td><code>BAD_SOURCE</code></td><td>No usable price was available, the returned price was zero, the optional uptime-feed check failed, or the two adaptor prices exceeded the configured bad-source bound.</td></tr></tbody></table>

`getPrice()` returns the error code to the caller. Market helper functions take an `errorCodeBreakpoint` and revert with `OracleManager__ErrorCodeFlagged()` when the observed code is greater than or equal to that breakpoint.

This makes the caller's tolerance explicit. For example, a caller that passes `BAD_SOURCE` accepts `CAUTION` but rejects `BAD_SOURCE`; a caller that passes `CAUTION` rejects both `CAUTION` and `BAD_SOURCE`.

### Market Query Helpers

Market-facing helpers price cTokens and account positions in the form used by liquidity checks.

| Function                 | Collateral pricing                                                                                                           | Debt pricing                                                                 | State behavior                                     |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------- |
| `getPriceIsolatedPair()` | Prices the collateral cToken's underlying with `getLower = true`, then converts to share value with `exchangeRateUpdated()`. | Prices the debt cToken's underlying with `getLower = false`.                 | Updates the collateral cToken exchange rate.       |
| `getPricesForMarket()`   | For snapshots marked `isCollateral`, prices the underlying with `getLower = true`, then converts to share value.             | For non-collateral snapshots, prices the underlying with `getLower = false`. | Calls `getSnapshotUpdated(account)` on each asset. |

Both helpers require each provided cToken to be registered in `cTokens`. If a cToken is not registered, the helper reverts with `OracleManager__NotSupported()`.

### Permissions and Configuration

Only addresses with elevated permissions in the central registry can configure the manager. These functions add or remove approved adaptors, attach approved adaptors to assets, configure two-adaptor deviation bounds, and register cTokens.

{% hint style="info" %}
Dual adaptor infrastructure exists on live deployments, but are unused in practice.
{% endhint %}

Deviation bounds are configured in basis points. The manager enforces:

| Constant                          | Value | Meaning                                                            |
| --------------------------------- | ----: | ------------------------------------------------------------------ |
| `MIN_DEVIATION_BOUND`             |  `20` | Minimum allowed caution bound, equal to 0.20%.                     |
| `MAX_DEVIATION_BOUND`             | `350` | Maximum allowed bad-source bound, equal to 3.50%.                  |
| `MIN_CAUTION_TO_BAD_SOURCE_DELTA` |  `20` | Minimum gap between caution and bad-source bounds, equal to 0.20%. |

When bounds are stored, the manager adds `BPS` internally. For example, an input of `200` is stored as `10200` so `_checkBounds()` can compare price ratios directly.

Configuration emits:

| Event                                                                                                                                                  | Emitted when                                    |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
| `AdaptorDependencyAdded(address asset, address adaptor)`                                                                                               | An adaptor dependency is added for an asset.    |
| `AdaptorDependencyRemoved(address asset, address adaptor)`                                                                                             | An adaptor dependency is removed from an asset. |
| `AssetDeviationBoundsSet(address asset, uint256 badSourceBoundUSD, uint256 cautionBoundUSD, uint256 badSourceBoundNative, uint256 cautionBoundNative)` | Two-adaptor deviation bounds are set.           |

### Integration Considerations

* Read real contract addresses from the deployment registry for the chain you are integrating with. Use placeholder addresses in examples and application config templates.
* Treat returned prices as 18-decimal values.
* Check both `price` and `errorCode` when calling `getPrice()` directly. A non-reverting call can still return `CAUTION` or `BAD_SOURCE`.
* Use the same conservative direction as protocol liquidity checks: lower prices for collateral, higher prices for debt.
* Use market helpers when reproducing market health checks, because they update snapshots and exchange rates where the protocol expects updated values.
* Do not assume a cToken can be priced directly until `cTokens(cToken)` returns a nonzero underlying and the underlying is supported.
* Do not hardcode adaptor behavior in integrations. The manager exposes adaptor addresses, but source-specific checks are implemented inside each adaptor.

### User Interaction Functions

### Price Reads

#### **getPrice()**

**Description:** Returns the price of an asset or registered cToken and an oracle-manager error code. This is a view function and does not update cToken exchange rates.

**Function signature:**

```solidity
function getPrice(
    address asset,
    bool inUSD,
    bool getLower
) public view returns (uint256 price, uint256 errorCode);
```

**Inputs:**

<table><thead><tr><th width="119">Type</th><th width="120">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>asset</code></td><td>Underlying asset or registered cToken to price.</td></tr><tr><td><code>bool</code></td><td><code>inUSD</code></td><td><code>true</code> for USD-denominated price, <code>false</code> for native-token-denominated price.</td></tr><tr><td><code>bool</code></td><td><code>getLower</code></td><td><code>true</code> to return the lower value in a two-adaptor setup, <code>false</code> to return the higher value.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="126">Type</th><th width="127">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>uint256</code></td><td><code>price</code></td><td>18-decimal price.</td></tr><tr><td><code>uint256</code></td><td><code>errorCode</code></td><td><code>0</code>, <code>1</code>, or <code>2</code>.</td></tr></tbody></table>

{% hint style="info" %}

* Reverts with `OracleManager__NotSupported()` if the asset has no configured adaptor.
* Returns `(0, BAD_SOURCE)` if the optional `SEQUENCER_ORACLE()` uptime-feed check fails.
* Converts registered cToken prices using `exchangeRate()`.
  {% endhint %}

***

#### **getPriceIsolatedPair()**

**Description:** Returns conservative prices for a collateral cToken and debt cToken pair. This function is used by market logic that needs collateral value in share terms and debt value in underlying terms.

**Function signature:**

```solidity
function getPriceIsolatedPair(
    address collateralToken,
    address debtToken,
    uint256 errorCodeBreakpoint
) external returns (
    uint256 collateralSharesPrice,
    uint256 debtUnderlyingPrice
);
```

**Inputs:**

<table><thead><tr><th width="118">Type</th><th width="212">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>collateralToken</code></td><td>Registered cToken used as collateral.</td></tr><tr><td><code>address</code></td><td><code>debtToken</code></td><td>Registered cToken used for debt.</td></tr><tr><td><code>uint256</code></td><td><code>errorCodeBreakpoint</code></td><td>Error code threshold that causes a revert.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="109">Type</th><th width="225">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>uint256</code></td><td><code>collateralSharesPrice</code></td><td>Collateral cToken share price after applying <code>exchangeRateUpdated()</code>.</td></tr><tr><td><code>uint256</code></td><td><code>debtUnderlyingPrice</code></td><td>Debt token underlying price.</td></tr></tbody></table>

{% hint style="info" %}

* Reverts with `OracleManager__ErrorCodeFlagged()` when an observed error code is greater than or equal to `errorCodeBreakpoint`.
* Reverts with `OracleManager__NotSupported()` if either cToken is not registered.
  {% endhint %}

***

#### **getPricesForMarket()**

**Description:** Returns updated account snapshots and conservative prices for a list of cTokens in a market.

**Function signature:**

```solidity
function getPricesForMarket(
    address account,
    address[] calldata assets,
    uint256 errorCodeBreakpoint
)
    external
    returns (AccountSnapshot[] memory, uint256[] memory, uint256);
```

**Inputs:**

<table><thead><tr><th width="205">Type</th><th width="207">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>account</code></td><td>Account to snapshot.</td></tr><tr><td><code>address[] calldata</code></td><td><code>assets</code></td><td>cToken addresses to price.</td></tr><tr><td><code>uint256</code></td><td><code>errorCodeBreakpoint</code></td><td>Error code threshold that causes a revert.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="250">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>AccountSnapshot[] memory</code></td><td>Updated cToken snapshots for <code>account</code>.</td></tr><tr><td><code>uint256[] memory</code></td><td>Prices aligned to <code>assets</code>.</td></tr><tr><td><code>uint256</code></td><td>Number of assets processed.</td></tr></tbody></table>

{% hint style="info" %}
Calls `getSnapshotUpdated(account)` on each asset. Prices collateral snapshots in cToken share terms and debt snapshots in underlying terms.
{% endhint %}

***

### Support and Status Reads

#### **getPricingAdaptors()**

**Description:** Returns the adaptor addresses currently configured for an asset.

**Function signature:**

```solidity
function getPricingAdaptors(
    address asset
) external view returns(address[] memory result);
```

**Inputs:**

<table><thead><tr><th width="120">Type</th><th width="107">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>asset</code></td><td>Underlying asset whose pricing adaptors should be returned.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="193">Type</th><th width="119">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address[] memory</code></td><td><code>result</code></td><td>Current adaptor dependencies for <code>asset</code>.</td></tr></tbody></table>

**Events:** None.

{% hint style="info" %}
This returns the dynamic adaptor array that the generated `assetPricingConfig()` getter omits.
{% endhint %}

***

#### **isSupportedAsset()**

**Description:** Returns whether an underlying asset or registered cToken is supported by the manager.

**Function signature:**

```solidity
function isSupportedAsset(address asset) external view returns (bool);
```

**Inputs:**

<table><thead><tr><th width="118">Type</th><th width="103">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>asset</code></td><td>Underlying asset or registered cToken to check.</td></tr></tbody></table>

Return data:

<table><thead><tr><th width="85">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>bool</code></td><td><code>true</code> if the asset has at least one configured pricing adaptor. For cTokens, support is checked through the registered underlying asset.</td></tr></tbody></table>

**Events:** None.

***

#### **isSequencerValid()**

**Description:** Returns whether the optional central-registry uptime-feed check currently permits pricing.

**Function signature:**

```solidity
function isSequencerValid() external view returns (bool);
```

**Inputs:** None.

**Return data:**

<table><thead><tr><th width="90">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>bool</code></td><td><code>true</code> when no uptime feed is configured or the configured feed currently passes the manager's check.</td></tr></tbody></table>

**Events:** None.

{% hint style="info" %}
`getPrice()` returns `(0, BAD_SOURCE)` when this check fails. Market helper functions revert with `OracleManager__ErrorCodeFlagged()` when this check fails.
{% endhint %}

***

#### **isApprovedAdaptor()**

**Description:** Returns whether an adaptor address is approved for manager use.

**Function signature:**

```solidity
function isApprovedAdaptor(address adaptor) external view returns (bool);
```

**Inputs:**

<table><thead><tr><th width="115">Type</th><th width="106">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>adaptor</code></td><td>Adaptor address to check.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="86">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>bool</code></td><td><code>true</code> if the adaptor is approved for manager use.</td></tr></tbody></table>

**Events:** None.

***

#### **cTokens()**

**Description:** Returns the underlying asset for a registered Curvance cToken, or `address(0)` if the token is not registered.

**Function signature:**

```solidity
function cTokens(address cToken) external view returns (address);
```

**Inputs:**

<table><thead><tr><th width="113">Type</th><th width="103">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>cToken</code></td><td>Curvance cToken address to inspect.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="110">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td>Registered underlying asset, or <code>address(0)</code> when <code>cToken</code> is not registered.</td></tr></tbody></table>

**Events:** None.

{% hint style="info" %}
`OracleManager` uses this mapping to decide whether `getPrice()` should price the input as a cToken share.
{% endhint %}

***

#### **assetPricingConfig()**

**Description:** Returns the stored deviation-bound fields for an asset. The generated getter does not return the dynamic adaptor array; use `getPricingAdaptors(asset)` for adaptor addresses.

**Function signature:**

```solidity
function assetPricingConfig(address asset) external view returns (
    uint16,
    uint16,
    uint16,
    uint16
);
```

**Inputs:**

<table><thead><tr><th width="115">Type</th><th width="95">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td><code>asset</code></td><td>Underlying asset whose stored deviation bounds should be read.</td></tr></tbody></table>

**Return data:**

<table><thead><tr><th width="120">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>uint16</code></td><td>Stored USD bad-source bound.</td></tr><tr><td><code>uint16</code></td><td>Stored USD caution bound.</td></tr><tr><td><code>uint16</code></td><td>Stored native-token bad-source bound.</td></tr><tr><td><code>uint16</code></td><td>Stored native-token caution bound.</td></tr></tbody></table>

**Events:** None.

{% hint style="info" %}
The stored values include the `BPS` offset added by `_setDeviationBounds()`.  Use `getPricingAdaptors(asset)` to read the configured adaptor addresses.
{% endhint %}

***

#### **centralRegistry()**

**Description:** Returns the central registry used for permission checks and optional uptime-oracle lookup.

**Function signature:**

```solidity
function centralRegistry() external view returns (ICentralRegistry);
```

**Inputs:** None.

**Return data:**

<table><thead><tr><th width="205">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ICentralRegistry</code></td><td>Central registry used by the manager.</td></tr></tbody></table>

**Events:** None.

***

#### **native()**

**Description:** Returns the sentinel address used for the chain's native gas token.

**Function signature:**

```solidity
function native() external view returns (address);
```

**Inputs:** None.

**Return data:**

<table><thead><tr><th width="118">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td>Sentinel address used to price the chain's native gas token.</td></tr></tbody></table>

**Events:** None.

{% hint style="info" %}
The sentinel is `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`.
{% endhint %}

***

### **Constant Getters**

**Description:** Return immutable oracle-manager configuration limits.

**Function signatures:**

```solidity
function GRACE_PERIOD_TIME() external view returns (uint256);
function MAX_DEVIATION_BOUND() external view returns (uint256);
function MIN_DEVIATION_BOUND() external view returns (uint256);
function MIN_CAUTION_TO_BAD_SOURCE_DELTA() external view returns (uint256);
```

**Inputs:** None.

**Return data:**

| Function                            | Type      | Description                                                |
| ----------------------------------- | --------- | ---------------------------------------------------------- |
| `GRACE_PERIOD_TIME()`               | `uint256` | Uptime-feed grace period, in seconds.                      |
| `MAX_DEVIATION_BOUND()`             | `uint256` | Maximum allowed deviation bound, in BPS.                   |
| `MIN_DEVIATION_BOUND()`             | `uint256` | Minimum allowed deviation bound, in BPS.                   |
| `MIN_CAUTION_TO_BAD_SOURCE_DELTA()` | `uint256` | Minimum gap between caution and bad-source bounds, in BPS. |
