Oracle - Accessing Price Feeds
Giving access to price feeds

1. Introduction

The Oracle contracts allow the protocol to get access to the price of a collateral with respect to a stablecoin or to the price of a stablecoin with respect to that of another stablecoin. The way the protocol is designed for the moment is that there will be one oracle contract per feed to access.
While the interface for each oracle contract is the same, the structure (and not just the references to the feeds to read) inside oracle contracts is going to vary from one contract to another.
Angle uses Chainlink price feeds and combines it with Uniswap V3 time-weighted average oracles with a 5 minutes window. The protocol always takes the price that is most at its advantage to prevent front-running. For instance, in a mint transaction where the protocol is looking at the price of a collateral expressed in stablecoin, the protocol chooses the minimal price between Chainlink and Uniswap. In a burn transaction where stablecoins need to be converted against collateral, the protocol chooses the highest price possible for the collateral with respect to the stablecoin (and thus the lowest price possible for the stablecoin in collateral).
There may be lots of different pairs to compute, for instance ETH-EUR, or USD-EUR, or wBTC-EUR. In some cases, to get an accurate Uniswap price from pools with liquidity, the protocol may need to use a circuit of Uniswap pools (like wBTC-ETH and then ETH-USDC for the price of ETH with respect to USD), or in cases like USD-EUR, the protocol may be unable to fetch this information from Uniswap and can just get it from Chainlink.
Given the diversity of needs in terms of oracle, to make each contract efficient and suited for its particular need, several different contracts have been coded, like for instance:
  • A single Chainlink feed: like USD-EUR used in the case of USDC-EUR
  • A single Chainlink feed with a Uniswap V3 circuit: for like wBTC-USD with Uniswap doing wBTC-ETH -> ETH-USDC and for Chainlink wBTC-USD
  • Multiple Chainlink feeds and a single Uniswap V3 pool: for like ETH - EUR, in this case the Uniswap price is computed by using just the pool ETH-USDC and combines it with Chainlink's USD-EUR feed. The Chainlink price is then obtained using the circuit ETH-USD and the same USD-EUR as the one that's used by Uniswap.
The protocol always considers that the price of 1 USDC on Uniswap is 1 USD.

2. Contracts Details


The way the code is organized is that it relies on some utils function to access the TWAP price of a Uniswap pool or of a Chainlink feed (utils/ChainlinkUtils.sol and utils/UniswapUtils.sol), there are then some modules that can be imported to make an oracle contract with Chainlink or Uniswap (modules/ModuleChainlinkMulti.sol, modules/ModuleChainlinkSingle.sol,modules/ModuleUniswapMulti.sol, modules/ModuleUniswapSingle.sol).
There is also an OracleAbstract.sol contract that defines some key functions that are found all across oracle contract.
Each oracle contract thus implements one or several modules, OracleAbstract and IOracle. There is for instance an OracleMulti contract (for an oracle looking at multiple Chainlink pools and Uniswap pools) or OracleChainlinkSingle (for an oracle contract relying on a single Chainlink pool)


Uniswap Parameters

  • twapPeriod: Time weigthed average window that should be used for each Uniswap rate. If a circuit of Uniswap rates is used, this value is the same across all circuits. 5 minutes is going to be the initial TWAP window
  • circuitUniswap or poolUniswap: Uniswap pool(s) used in the oracle contract. If several pools are specified, the addresses should be given in the order with which they are going to be treated
  • circuitUniIsMultiplied or isUniMultiplied: Whether the rate of the corresponding pool(s) should be multiplied or divided to the previous value to convert
  • circuitChainlink or poolChainlink: Chainlink pool(s) to look for in the contract
  • circuitChainIsMultiplied or isChainlinkMultiplied: Whether each rate for the pairs in circuitChainlink should be multiplied or divided to the previous amount
  • chainlinkDecimals: Decimals for each Chainlink pairs

Global Parameters

  • inBase: Number of decimals of the in-currency
  • BASE : Base used for computation (it is the same as the one used across all contracts of the protocol for the tokens)
  • description: Description for what the oracle does

Access Control

  • GUARDIAN_ROLE: Used in oracle contracts involving Uniswap. There is no need for a GOVERNOR_ROLE here as governor is guardian

3. Key Mechanisms & Concepts

Read Functions

All the functions below are view functions which can be accessed by anyone. A rate refers to how much 1 of in-currency would be worth in out-currency. A quote amount is how much x where x is the quote amount of the in-currency would be worth in out-currency.
In all cases, the output value of these read functions is expressed not in the base of the out currency but in the base BASE(with 18 decimals). This means that for instance if the out-currency is ETH with 18 decimals, and if the in-currency is USDC with 6 decimals, if the 1 ETH is worth 1000 USDC, then the rate ETH-USD is 10**21. And conversely, the rate USDC-ETH is 10**15.


function read() external view returns (uint256 rate);
Reads one of the rates from the circuits given. By default if the oracle involves a Uniswap price and a Chainlink price, this function will return the Uniswap price.


function readAll() external view returns (uint256, uint256);
Read rates from the circuit of both Uniswap and Chainlink if there are both circuits involved in the oracle contract else returns twice the same price. In case where the concerned oracle contract involves both Chainlink and Uniswap, this function returns the lowest rate first.


function readLower(uint256 lower) external view returns (uint256);
Reads rates from the circuit of both Uniswap and Chainlink if there are both circuits and returns either the highest of both rates or the lowest. If there is only one rate computed in an oracle contract, then the only rate is returned and this function is equivalent to the read() function regardless of the value of the lower parameter.
  • lower: Whether the lower or the upper price should be returned by the function


function readQuote(uint256 quoteAmount) external view returns (uint256);
Converts an in-currency quote amount to out currency using one of the rates available in the oracle contract. Like in the read() function, if the oracle involves a Uniswap and a Chainlink price, this function will use the Uniswap price.
  • quoteAmount: Amount (in the input collateral) to be converted to be converted in out currency


function readQuoteLower(uint256 quoteAmount) external view returns (uint256);
Returns the lowest quote amount between Uniswap and Chainlink circuits (if possible). If the oracle contract only involves a single feed, then this returns the value of this feed


function getInBase() external view returns (uint256);
Gets the base of the input currency

Uniswap Governance Function

For oracle contracts involving Uniswap V3 prices (most of the time Uniswap V3 will be used along Chainlink prices), governance has the ability to change the Twap period through the changeTwapPeriod() method.