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 EndpointReporter KeySupported Tokens

Coinbase Pro

0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC

BTC, ETH, DAI, REP, ZRX, BAT, KNC, LINK, COMP

OKEx*

0x419c555b739212684432050b7ce459ea8e7b8bda

BTC, ETH

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 nameaddress

OpenOraclePriceData

0xc629c26dced4277419cde234012f8160a0278a79

UniswapAnchoredView

0x9b8eb8b3d6e2e0db36f41455185fef7049a35cae

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();