Introduction
The Open Price Feed allows Reporters to sign price data using a known public key, which Posters (any Ethereum address) can submit on-chain. The protocol codebase is hosted on GitHub, and maintained by the community.
Coinbase Pro is the first Reporter using the Open Price Feed format, signing prices every few minutes for assets traded on the exchange. Additional exchanges are developing their own Reporters for their own trading pairs, and new views can be deployed to use their prices.
Reporter Endpoint | Reporter Key | Supported Tokens |
---|---|---|
0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC | BTC, ETH, DAI, REP, ZRX, BAT, KNC, LINK, COMP, UNI | |
OKEx* | 0x419c555b739212684432050b7ce459ea8e7b8bda | BTC, ETH, DAI, ZRX, BAT, KNC, LINK, COMP |
The above endpoints are also mirrored at prices.compound.finance.
* Signing and publishing prices, but not yet a designated reporter in UniswapAnchoredView.
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 contracts can be found on-chain as follows:
Contract name | address |
---|---|
OpenOraclePriceData | |
UniswapAnchoredView |
Architecture
The Open Price Feed consists of two main contracts.
- OpenOraclePriceData is a neutral price storage contract that stores prices signed by any address.
- 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.
Post Prices
Posts an array of prices to OpenOraclePriceData and saves them to UniswapAnchoredView if the prices are within a bound of the anchor price and are signed by a reporter. Prices are expected to be in USD with 6 decimals of precision.
- messages[]: The array of prices to sign. Each message is a bytes[] with the format: (string memory kind, uint64 timestamp, string memory key, uint64 value)
- signatures[]: An array of signatures corresponding to the messages. Each signature is a bytes[] with the format (bytes32 r, bytes32 s, uint8 v).
- symbols[]: An array of symbols corresponding to the price messages.
Post prices manually from the browser with this notebook.
UniswapAnchoredView
function postPrices(bytes[] calldata messages, bytes[] calldata signatures, string[] calldata symbols)
Web3 1.0
const view = UniswapAnchoredView.at(0xABCD...);
const res = await fetch("https://prices.compound.finance");
const {coinbase, okex} = await res.json();
const symbols = ['BTC', 'ETH', 'DAI', 'REP', 'ZRX', 'BAT', 'KNC', 'LINK', 'COMP'];
await view.methods.postPrices(coinbase.messages, coinbase.signatures, symbols).send({from: ...});
Price
Get the most recent price for a token in USD with 6 decimals of precision.
- symbol: Symbol as a string
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 18 decimals of precision.
- cToken: The address of the cToken contract of the underlying asset.
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.
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");
Invalidate Reporter
Posts a message signed by the reporter that invalidates the reporter's prices and causes the anchor price to be used directly instead. To be used in cases of emergency, if the reporter thinks their key may be compromised. Any Ethereum address can send the invalidation message.
- message: The invalidation message
- signature: A signature of the invalidation message
UniswapAnchoredView
function invalidateReporter(bytes memory message, bytes memory signature) external
Web3 1.0
const view = UniswapAnchoredView.at(0xABCD...);
await view.methods.invalidateReporter(message, signature).call();
Reporter
Get the reporter's address. Currently Coinbase Pro's address.
UniswapAnchoredView
function reporter() returns (uint)
Web3 1.0
const view = UniswapAnchoredView.at(0xABCD...);
const reporter = await view.methods.reporter().call();
Solidity
UniswapAnchoredView view = UniswapAnchoredView(0xABCD...);
uint reporter = view.reporter();
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();