# Euler Price Oracles Euler Price Oracles is a library of modular oracle adapters and components that implement `IPriceOracle`, a quote-based interface compatible with [ERC-7726](https://eips.ethereum.org/EIPS/eip-7726). - To read more about the design and motivation behind `IPriceOracle` and the oracles in this repo, check out the [whitepaper](docs/whitepaper.md) and [ERC-7726](https://eips.ethereum.org/EIPS/eip-7726). - To understand how Price Oracles fit into the [Euler Vault Kit](https://github.com/euler-xyz/euler-vault-kit), check out the [price oracles section](https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles) of the EVK whitepaper. - To use or develop with Euler Price Oracles, check out the [Usage](#usage) section. - To find out ways to contribute to Euler Price Oracles, check out the [Contributing](#contributing) section. - To observe 200+ live deployments of these oracle adapters check out the dedicated [Oracle Dashboard](https://oracles.euler.finance/). Euler Price Oracles has been [audited](audits/) by Spearbit, OpenZeppelin, ChainSecurity, Omniscia, yAudit, and Cantina. ## `IPriceOracle` All contracts in this library implement the `IPriceOracle` interface. ```solidity /// @return outAmount The amount of `quote` that is equivalent to `inAmount` of `base`. function getQuote( uint256 inAmount, address base, address quote ) external view returns (uint256 outAmount); /// @return bidOutAmount The amount of `quote` you would get for selling `inAmount` of `base`. /// @return askOutAmount The amount of `quote` you would spend for buying `inAmount` of `base`. function getQuotes( uint256 inAmount, address base, address quote ) external view returns (uint256 bidOutAmount, uint256 askOutAmount); ``` This interface shapes oracle interactions in an important way: it forces the consumer to think in [amounts rather than prices.](https://hackernoon.com/getting-prices-right) A subset of this interface (`getQuote`) has been standardized in [ERC-7726](https://eips.ethereum.org/EIPS/eip-7726). ### Quotes Euler Price Oracles are unique in that they expose a flexible quoting interface instead of reporting a static price. > [!NOTE] > Imagine a Chainlink price feed which reports the value `1 EUL/ETH`, the _unit price_ of `EUL`. Now consider an `IPriceOracle` adapter for the feed. It will fetch the unit price, multiply it by `inAmount`, and return the quantity `inAmount EUL/ETH`. We call this a _quote_ as it functionally resembles a swap on a decentralized exchange. The quoting interface offers several benefits to consumers: - **More intuitive queries:** Oracles are commonly used in DeFi to determine the value of assets. `getQuote` does exactly that. - **More expressive interface:** The unit price is a special case of a quote where `inAmount` is one whole unit of `base`. - **Safe and flexible integrations:** Under `IPriceOracle` adapters are internally responsible for converting decimals. This allows consumers to decouple themselves from a particular provider as they can remain agnostic to its implementation details. ### Bid/Ask Pricing Euler Price Oracles additionally expose `getQuotes` which returns two prices: the selling price (bid) and the buying price (ask). Bid/ask prices are inherently safer to use in lending markets as they can accurately reflect instantaneous price spreads. While few oracles support bid/ask prices currently, we anticipate their wider adoption in DeFi as on-chain liquidity matures. Importantly `getQuotes` allows for custom pricing strategies to be built under the `IPriceOracle` interface: - Querying two oracles and returning the lower and higher prices. - Reporting two prices from a single source e.g. a TWAP and a [median.](https://github.com/euler-xyz/median-oracle) - Applying a synthetic spread or a volatility-dependent confidence interval around a mid-price. ## Oracle Adapters An adapter is a minimal, fully immutable contract that queries an external price feed. It is the atomic building block of the Euler Price Oracles library. ### Design Principles The `IPriceOracle` interface is permissive in that it does not prescribe a particular way to implement it. However the adapters in this library adhere to a strict set of rules that we believe are necessary to enable safe, open, and self-governed markets to flourish. #### Immutable Adapters are fully immutable without governance or upgradeability. #### Minimally Responsible An adapter connects to one pricing system and queries a single price feed in that system. #### Bidirectional An adapter works in both directions. If it supports quoting `X/Y` it must also support `Y/X`. #### Observable An adapter's parameters and acceptance logic are easily observed on-chain. ### Summary of Adapters | Adapter | Type | Method | Supported Pairs | Parameters | | ------------------------------------------------------------------- | -------- | ------ | ----------------------- | -------------------------------------------- | | [ChainlinkOracle](src/adapter/chainlink/ChainlinkOracle.sol) | External | Push | Provider feeds | feed, max staleness | | [ChronicleOracle](src/adapter/chainlink/ChronicleOracle.sol) | External | Push | Provider feeds | feed, max staleness | | [PythOracle](src/adapter/pyth/PythOracle.sol) | External | Pull | Provider feeds | feed, max staleness, max confidence interval | | [RedstoneCoreOracle](src/adapter/redstone/RedstoneCoreOracle.sol) | External | Pull | Provider feeds | feed, max staleness, cache ttl | | [LidoOracle](src/adapter/lido/LidoOracle.sol) | Onchain | Rate | wstETH/stETH | - | | [LidoFundamentalOracle](src/adapter/lido/LidoFundamentalOracle.sol) | Onchain | Rate | wstETH/ETH | - | | [UniswapV3Oracle](src/adapter/uniswap/UniswapV3Oracle.sol) | Onchain | TWAP | UniV3 pools | fee, twap window | | [PendleOracle](src/adapter/pendle/PendleOracle.sol) | Onchain | TWAP | Pendle markets | pendle market, twap window | | [RateProviderOracle](src/adapter/rate/RateProviderOracle.sol) | Onchain | Rate | Balancer rate providers | rate provider | | [FixedRateOracle](src/adapter/fixed/FixedRateOracle.sol) | Onchain | Rate | Any | rate | ## Usage ### Install To install Price Oracles in a [Foundry](https://github.com/foundry-rs/foundry) project: ```sh forge install euler-xyz/euler-price-oracle ``` ### Development Clone the repo: ```sh git clone https://github.com/euler-xyz/euler-price-oracle.git && cd euler-price-oracle ``` Install forge dependencies: ```sh forge install ``` [Optional] Install Node.js dependencies: ```sh npm install ``` Compile the contracts: ```sh forge build ``` ### Testing The repo contains 4 types of tests: unit, property, bounds, fork, identified by their filename suffix. #### Fork Tests To run fork tests set the `ETHEREUM_RPC_URL` variable in your environment: ```sh # File: .env ETHEREUM_RPC_URL=... ``` Alternatively, to exclude fork tests: ```sh forge test --no-match-contract Fork ``` > [!IMPORTANT] > Tests in `RedstoneCoreOracle.fork.t.sol` use the [`ffi`](https://book.getfoundry.sh/cheatcodes/ffi#ffi) cheatcode to invoke a script that retrieves Redstone update data. FFI mode is **not enabled by default** for safety reasons. To run the Redstone Fork tests set `ffi = true` in `foundry.toml`. ## Contributing Euler Price Oracles is a [free and open-source](LICENSE) public good. We encourage you to engage and contribute. Feel free to [open](https://github.com/euler-xyz/euler-price-oracle/issues/new) a GitHub issue discussing your ideas. > [!TIP] > Submit testing- and documentation-related PRs to [`development`](https://github.com/euler-xyz/euler-price-oracle/tree/master) and changes under `src/` to [`experiments`](https://github.com/euler-xyz/euler-price-oracle/tree/experiments). Here are a few ideas how you can improve Euler Price Oracles: ### Research and Development - Write an adapter for a new [oracle provider](https://defillama.com/oracles/chain/Ethereum) or an AMM such as [Curve V2](https://resources.curve.fi/factory-pools/understanding-oracles/#exponential-moving-average). - `getQuotes` returns bid/ask prices, however we are not aware of any oracle providers that currently support them. Write an `IPriceOracle` wrapper that applies a price spread around a mid-point price. The spread could be dynamic based on proxy metrics such as liquidity, volume, (implied) volatility, correlation. We are highly interested in research towards this direction. - ZK Coprocessors like [Axiom](https://www.axiom.xyz/) and [Lagrange](https://www.lagrange.dev/) allow you to [verifiably compute](https://blog.axiom.xyz/what-is-a-zk-coprocessor/) over historical blockchain state in ZK circuits. This unlocks a new design space for trust-minimized manipulation-resistant oracles. Write a proof-of-concept oracle using a ZK Coprocessor. Some ideas: an [implied volatility oracle](https://lambert-guillaume.medium.com/on-chain-volatility-and-uniswap-v3-d031b98143d1) based on Uniswap V3, a [median filtering oracle](https://github.com/euler-xyz/median-oracle) over an AMM. - Write an `IPriceOracle` wrapper that implements a trustless circuit-breaker mechanism that detects failure conditions. Upon detection it could switch to another oracle or redeploy the adapter with different parameters. - Write an alternative router to `EulerRouter` that supports more flexible configuration. - Research whether a DEX aggregator API can be used as a pull-based price oracle and write a proof-of-concept adapter. - Some oracle providers are compatible with Chainlink's `AggregatorV3Interface` either directly or through a facade contract. Are they safe to use through `ChainlinkOracle` in this library? - Write a sanity checking script that verifies an adapter is correctly configured by comparing the quote against a price API. - Write a simulation script that generates a line plot comparing a given adapter's prices against a price API historically. ### Security - Expand the fork test suite to include more pairs and to historically backtest the adapter. - Write fuzz and invariant tests using [echidna](https://github.com/crytic/echidna), [medusa](https://github.com/crytic/medusa), or [foundry](https://book.getfoundry.sh/forge/invariant-testing). - Write formal verification tests using [Certora](https://docs.certora.com/en/latest/), [halmos](https://github.com/a16z/halmos), or [kontrol](https://github.com/runtimeverification/kontrol). - `EulerRouter` can price ERC4626 shares to assets by calling [`convertToAssets`](https://eips.ethereum.org/EIPS/eip-4626#converttoassets). Which of the [currently live](https://erc4626.info/vaults/) vaults have a manipulation-resistant pricing function? - With pull-based oracles users control the price update flow. What is an appropriate value for `maxStaleness` on Ethereum considering network delays and possible censorship? Are there ways `maxStaleness` can be safely reduced? - Are these oracles readily usable on the various L2s or are there additional considerations that must be had? ### Technical Documentation - Write an smart contract integration guide for Euler Price Oracles. - Write a frontend integration guide for fetching the price of pull-based oracles. - Write or compile risk research for a provider, detailing how it works, its failure modes and trust assumptions. ## Safety This software is **experimental** and is provided "as is" and "as available". **No warranties are provided** and **no liability will be accepted for any loss** incurred through the use of this codebase. Always include thorough tests when using Euler Price Oracles to ensure it interacts correctly with your code. ## License (c) 2024 Euler Labs Ltd. The Euler Price Oracles code is licensed under the [GPL-2.0-or-later](LICENSE) license.