Introduction
The Open Price Feed accounts price data for the Compound protocol. The protocol's Comptroller contract uses it as a source of truth for prices. Prices are updated by Chainlink Price Feeds. The codebase is hosted on GitHub, and maintained by the community.
The Compound Protocol uses a View contract ("Price Feed") which verifies that reported prices fall within an acceptable bound of the time-weighted average price of the token/ETH pair on Uniswap v2, a sanity check referred to as the Anchor price.
The Chainlink price feeds submit prices for each cToken through an individual ValidatorProxy contract. Each ValidatorProxy is the only valid reporter for the underlying asset price. The contracts can be found on-chain as follows:
Contract name | address |
---|---|
AAVE ValidatorProxy | |
BAT ValidatorProxy | |
COMP ValidatorProxy | |
DAI ValidatorProxy | |
ETH ValidatorProxy | |
FEI ValidatorProxy | |
FRAX ValidatorProxy | |
LINK ValidatorProxy | |
LUSD ValidatorProxy | |
MATIC ValidatorProxy | |
MKR ValidatorProxy | |
RAI ValidatorProxy | |
REP ValidatorProxy | |
SUSHI ValidatorProxy | |
UNI ValidatorProxy | |
WBTC ValidatorProxy | |
YFI ValidatorProxy | |
ZRX ValidatorProxy | |
UniswapAnchoredView |
Architecture
The Open Price Feed consists of two main contracts.
- ValidatorProxy is a contract that calls validate on the UniswapAnchoredView. This queries Uniswap v2 to check if a new price is within the Uniswap v2 TWAP anchor. If valid, the UniswapAnchoredView is updated with the asset's price. If invalid, the price data is not stored.
- UniswapAnchoredView only stores prices that are within an acceptable bound of the Uniswap time-weighted average price and are signed by a reporter. Also contains logic that upscales the posted prices into the format that Compound's Comptroller expects.
This architecture allows multiple views to use the same underlying price data, but to verify the prices in their own way.
Stablecoins like USDC, USDT, and TUSD are fixed at $1. SAI is fixed at 0.005285 ETH.
As a precaution, the Compound community multisig has the ability to engage a failover that will switch a market's primary oracle from the Chainlink Price Feeds to Uniswap v2. The multisig is able to change to a failover for single markets. The Uniswap V2 TWAP price is used as the failover. The community can enable or disable the failover using activateFailover or deactivateFailover.
Price
Get the most recent price for a token in USD with 6 decimals of precision.
- symbol: Symbol as a string
- RETURNS: The price of the asset in USD as an unsigned integer scaled up by 10 ^ 6.
UniswapAnchoredView
function price(string memory symbol) external view returns (uint)
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint price = view.price("ETH");
Web3 1.0
const view = UniswapAnchoredView.at("0xABCD...");
//eg: returns 200e6
const price = await view.methods.price("ETH").call();
Underlying Price
Get the most recent price for a token in USD with 6 decimals of precision.
- cToken: The address of the cToken contract of the underlying asset.
- RETURNS: The price of the asset in USD as an unsigned integer scaled up by 10 ^ (36 - underlying asset decimals). E.g. WBTC has 8 decimal places, so the return value is scaled up by 1e28.
UniswapAnchoredView
function getUnderlyingPrice(address cToken) external view returns (uint)
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint price = view.getUnderlyingPrice(0x1230...);
Web3 1.0
const view = UniswapAnchoredView.at("0xABCD...");
//eg: returns 400e6
const price = await view.methods.getUnderlyingPrice("0x1230...").call();
Config
Each token the Open Price Feed supports needs corresponding configuration metadata. The configuration for each token is set in the constructor and is immutable.
The fields of the config are:
- cToken: The address of the underlying token's corresponding cToken. This field is null for tokens that are not supported as cTokens.
- underlying: Address of the token whose price is being reported.
- symbolHash: The keccak256 of the byte-encoded string of the token's symbol.
- baseUnit: The number of decimals of precision that the underlying token has. Eg: USDC has 6 decimals.
- PriceSource: An enum describing the whether or not to special case the prices for this token. FIXED_ETH is used to set the SAI price to a fixed amount of ETH, and FIXED_USD is used to peg stablecoin prices to $1. REPORTER is used for all other assets to indicate the reported prices and Uniswap anchoring should be used.
- fixedPrice: The fixed dollar amount to use if PriceSource is FIXED_USD or the number of ETH in the case of FIXED_ETH (namely for SAI).
- uniswapMarket: The token's market on Uniswap, used for price anchoring. Only filled if PriceSource is REPORTER.
- isUniswapReversed: A boolean indicating the order of the market's reserves.
- reporter: The address that submits prices for a particular cToken. This is always a ValidatorProxy contract that is always called by a price feed reference contract for each relevant price update posted by Chainlink oracle nodes.
- reporterMultiplier: An unsigned integer that is used to transform the price reported by the Chainlink price feeds to the correct base unit that the UniswapAnchoredView expects. This is required because the price feeds report prices with different decimal placement than the UniswapAnchoredView.
UniswapAnchoredView
function getTokenConfigBySymbol(string memory symbol) public view returns (TokenConfig memory)
function getTokenConfigBySymbolHash(bytes32 symbolHash) public view returns (TokenConfig memory)
function getTokenConfigByCToken(address cToken) public view returns (TokenConfig memory)
Web3 1.0
const view = UniswapAnchoredView.at("0xABCD...");
const config = await view.methods.getTokenConfigBySymbol("ETH").call();
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint price = view.getTokenConfigBySymbol("ETH");
Anchor Period
Get the anchor period, the minimum amount of time in seconds over which to take the time-weighted average price from Uniswap.
####UniswapAnchoredView
function anchorPeriod() returns (uint)
Web3 1.0
const view = UniswapAnchoredView.at("0xABCD...");
const anchorPeriod = await view.methods.anchorPeriod().call();
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint anchorPeriod = view.anchorPeriod();
Anchor Bounds
Get the highest and lowest ratio of the reported price to the anchor price that will still trigger the price to be updated. Given in 18 decimals of precision (eg: 90% => 90e16).
####UniswapAnchoredView
function upperBoundAnchorRatio() returns (uint)
function lowerBoundAnchorRatio() returns (uint)
Web3 1.0
const view = UniswapAnchoredView.at("0xABCD...");
const upperBoundRatio = await view.methods.upperBoundAnchorRatio().call();
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint upperBoundRatio = view.upperBoundAnchorRatio();