# Price Oracle Middleware

## Purpose and scope

The Price Oracle Middleware system provides standardized asset valuation and conversion for the PlasmaVault ecosystem. It is centered around the `PriceOracleMiddlewareWithRoles` contract, which acts as a centralized price hub, and the `PriceOracleMiddlewareManager`, which provides a vault-specific interface for price queries and validation.

The system is responsible for:

* Providing asset prices in a unified quote currency (USD) with 18 decimals [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol42-46](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L42-L46)
* Managing custom price feed sources (e.g., Pendle PT, Curve, ERC4626) [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol125-135](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L125-L135)
* Falling back to the Chainlink Feed Registry when custom sources are not defined [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol19-21](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L19-L21)
* Implementing price change validation to protect vaults from extreme volatility or oracle manipulation [contracts/managers/price/PriceOracleMiddlewareManagerLib.sol43-48](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/managers/price/PriceOracleMiddlewareManagerLib.sol#L43-L48)

## System architecture

The architecture separates global price discovery from vault-specific price management and validation.

### Logic flow and code entities

<figure><img src="https://1011095567-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoaErR6oxxmjeJRJYOuXH%2Fuploads%2FJ1JXaNHJMLDkMdopTngT%2Fimage.png?alt=media&#x26;token=148d17d9-f3bc-47c5-8d98-901aa3874a38" alt=""><figcaption></figcaption></figure>

**Architecture description**:

1. **PlasmaVault** and its **Balance Fuses** interact with the `PriceOracleMiddlewareManager` assigned to the vault [contracts/vaults/PlasmaVault.sol75](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/vaults/PlasmaVault.sol#L75-L75)
2. The **PriceOracleMiddlewareManager** handles vault-specific configuration, such as custom asset sources and price validation logic stored via `PriceOracleMiddlewareManagerLib` [contracts/managers/price/PriceOracleMiddlewareManagerLib.sol64-75](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/managers/price/PriceOracleMiddlewareManagerLib.sol#L64-L75)
3. **PriceOracleMiddlewareWithRoles** serves as the protocol-wide price aggregator, managing specialized feeds like **PtPriceFeed** for Pendle tokens [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol140-150](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L140-L150)

## Core components

### 1. PriceOracleMiddlewareWithRoles

This contract provides the "Source of Truth" for asset prices. It normalizes all outputs to 18 decimals (WAD) [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol76-77](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L76-L77)

* **Quote Currency**: Hardcoded to the Chainlink USD address `0x...0348` [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol42](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L42-L42)
* **Custom Sources**: Allows administrators to map specific assets to custom `IPriceFeed` implementations [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol112-114](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L112-L114)
* **Pendle Integration**: Features specialized logic to deploy and manage `PtPriceFeed` instances for Pendle Principal Tokens [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol140-160](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L140-L160)

### 2. PriceOracleMiddlewareManager

A per-vault manager that wraps the global middleware with validation and vault-specific overrides.

* **getAssetPrice**: The primary entry point. It checks for a vault-specific source in `PriceOracleMiddlewareManagerLib` before falling back to the global middleware [contracts/managers/price/PriceOracleMiddlewareManager.sol103-118](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/managers/price/PriceOracleMiddlewareManager.sol#L103-L118)
* **Validation**: Implements `validatePriceChange` which compares the current price against a `lastValidatedPrice`. If the delta exceeds `maxPriceDelta`, the transaction reverts [contracts/managers/price/PriceOracleMiddlewareManagerLib.sol43-48](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/managers/price/PriceOracleMiddlewareManagerLib.sol#L43-L48)

## Specialized price feeds

The system supports various specialized feeds to handle complex DeFi assets.

### Pendle PT Price Feed (`PtPriceFeed`)

Calculates the price of Pendle Principal Tokens using Pendle's TWAP oracle and the price of the underlying asset from the middleware [contracts/price\_oracle/price\_feed/PtPriceFeed.sol12-18](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/price_feed/PtPriceFeed.sol#L12-L18)

| Feature     | Implementation Detail                                                                                                                                                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TWAP Window | Minimum 5 minutes, recommended 15 minutes [contracts/price\_oracle/price\_feed/PtPriceFeed.sol23](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/price_feed/PtPriceFeed.sol#L23-L23)                   |
| Calculation | `(PtToAssetRate * UnderlyingAssetPrice) / scalingFactor` [contracts/price\_oracle/price\_feed/PtPriceFeed.sol128](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/price_feed/PtPriceFeed.sol#L128-L128) |
| Metadata    | Returns Chainlink-compatible `latestRoundData` [contracts/price\_oracle/price\_feed/PtPriceFeed.sol109-113](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/price_feed/PtPriceFeed.sol#L109-L113)       |

### Feed factory pattern

Factories like `PtPriceFeedFactory` and `CurveStableSwapNGPriceFeedFactory` are used to deploy standardized feed instances that the middleware can then consume [contracts/factory/price\_feed/PtPriceFeedFactory.sol6-10](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/factory/price_feed/PtPriceFeedFactory.sol#L6-L10)

## Price validation mechanism

The validation system prevents the vault from transacting at "stale" or manipulated prices by enforcing a maximum allowed deviation.

<figure><img src="https://1011095567-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoaErR6oxxmjeJRJYOuXH%2Fuploads%2FyGus5wJ77KAhAtNNFwY3%2Fimage.png?alt=media&#x26;token=b28bf0fe-2d97-49fe-8dfc-0f616629ffc9" alt=""><figcaption></figcaption></figure>

* **Configuration**: Managed via `updatePriceValidation` which sets the `maxPriceDelta` for an asset [contracts/managers/price/PriceOracleMiddlewareManagerLib.sol139-152](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/managers/price/PriceOracleMiddlewareManagerLib.sol#L139-L152)
* **Execution**: Usually triggered via a Pre-Hook (`ValidateAllAssetsPricesPreHook`) before vault actions like `execute` or `deposit` [contracts/handlers/pre\_hooks/pre\_hooks/ValidateAllAssetsPricesPreHook.sol18-23](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/handlers/pre_hooks/pre_hooks/ValidateAllAssetsPricesPreHook.sol#L18-L23)

## Access control and roles

The system utilizes a hierarchy of roles defined in `Roles.sol` to secure configuration.

| Role                                   | Entity                           | Permission                                                                                                                                                                                                                      |
| -------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ADMIN_ROLE`                           | `AccessManager`                  | Highest level; manages all roles [contracts/libraries/Roles.sol11-13](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/libraries/Roles.sol#L11-L13)                                                             |
| `ATOMIST_ROLE`                         | `PlasmaVault`                    | Can update the `priceOracleMiddleware` address [contracts/libraries/Roles.sol44-45](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/libraries/Roles.sol#L44-L45)                                               |
| `PRICE_ORACLE_MIDDLEWARE_MANAGER_ROLE` | `PriceOracleMiddlewareManager`   | Manages asset price sources and validation deltas [contracts/libraries/Roles.sol102-105](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/libraries/Roles.sol#L102-L105)                                        |
| `SET_ASSETS_PRICES_SOURCES`            | `PriceOracleMiddlewareWithRoles` | Global permission to set price sources [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol27](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L27-L27) |
| `ADD_PT_TOKEN_PRICE`                   | `PriceOracleMiddlewareWithRoles` | Permission to deploy Pendle PT feeds [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol28](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L28-L28)   |

## Data normalization

All prices returned by the middleware system are normalized to 18 decimals (WAD) to ensure consistency across the IPOR Fusion protocol.

* **Normalization Formula**: If a feed has $$N$$ decimals, the price is scaled by $$10^{(18-N)}$$ [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol76-77](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L76-L77)
* **Batch Processing**: `getAssetsPrices` allows fetching multiple asset prices in a single call, returning arrays of prices all in 18-decimal format [contracts/price\_oracle/PriceOracleMiddlewareWithRoles.sol92-107](https://github.com/IPOR-Labs/ipor-fusion/blob/c26a0a9d/contracts/price_oracle/PriceOracleMiddlewareWithRoles.sol#L92-L107)
