Skip to content

Commit

Permalink
Add a uniswap anchored view
Browse files Browse the repository at this point in the history
This commit adds a Uniswap anchored Open Oracle view.
We intend to propose switching the Compound Protocol to use this oracle, together with the Coinbase reporter feed.

The idea is that the price can only be set to a value coming from the reporter, but must be reasonably close to the anchor price.
The configuration for the oracle is passed up entirely in the constructor, including which uniswap market to use as an anchor for each reported price.

Tokens can also be configured to have FIXED_ETH or FIXED_USD prices.
FIXED_USD prices are constant (since the oracle returns prices denominated in USD).
FIXED_ETH prices are a multiple of the ETH price, which must be reported in order to be used.

The view also directly exposes the interface for compatibility with the Compound Protocol, which currently expects to fetch prices by CToken address.

In the unexpected event that the reporter believes their key to be compromised, they may sign a 'rotate' message which can be used to invalidate the reporter.
In such an event, the view is configured to fall back to using the anchor price directly.

In order to track the Uniswap price, the price accumulators those markets expose are tracked by block timestamp.
Whenever prices are posted, the tracked accumulators are updated in such a way as to always have an accumulator from at least a certain period ago.
When a current anchor price is needed, it is always the time-weighted average price over at least the configured anchor period.
  • Loading branch information
toninorair authored and jflatow committed Jul 4, 2020
1 parent c4c4683 commit d0a0d03
Show file tree
Hide file tree
Showing 27 changed files with 2,150 additions and 121 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
docker_layer_caching: true
- run:
|
sudo wget https://github.com/ethereum/solidity/releases/download/v0.6.6/solc-static-linux -O /usr/local/bin/solc
sudo wget https://github.com/ethereum/solidity/releases/download/v0.6.10/solc-static-linux -O /usr/local/bin/solc
sudo chmod +x /usr/local/bin/solc
- checkout
- restore_cache:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM node:13.6.0-alpine3.10
WORKDIR /open-oracle
RUN wget https://github.com/ethereum/solidity/releases/download/v0.6.6/solc-static-linux -O /usr/local/bin/solc && chmod +x /usr/local/bin/solc
RUN wget https://github.com/ethereum/solidity/releases/download/v0.6.10/solc-static-linux -O /usr/local/bin/solc && chmod +x /usr/local/bin/solc
RUN apk update && apk add --no-cache --virtual .gyp \
python \
make \
Expand Down
14 changes: 7 additions & 7 deletions contracts/AnchoredView/AnchoredView.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;

import "./SymbolConfiguration.sol";
Expand Down Expand Up @@ -45,14 +47,13 @@ contract AnchoredView is SymbolConfiguration {
/// @notice The Open Oracle Price Data contract
OpenOraclePriceData public immutable priceData;

/// @notice The highest ratio of the new median price to the anchor price that will still trigger the median price to be updated
/// @dev The highest ratio of the new median price to the anchor price that will still trigger the median price to be updated
uint immutable upperBoundAnchorRatio;

/// @notice The lowest ratio of the new median price to the anchor price that will still trigger the median price to be updated
/// @dev The lowest ratio of the new median price to the anchor price that will still trigger the median price to be updated
uint immutable lowerBoundAnchorRatio;

/// @notice Average blocks per day, for checking anchor staleness
/// @dev 1 day / 15
/// @dev Average blocks per day, for checking anchor staleness (1 day / 15)
uint constant blocksInADay = 5760;

/// @notice The event emitted when the median price is updated
Expand Down Expand Up @@ -114,7 +115,6 @@ contract AnchoredView is SymbolConfiguration {

uint reporterPrice = priceData.getPrice(reporter, tokenConfig.openOracleKey);
uint anchorPrice = getAnchorInUsd(tokenConfig, usdcPrice);

uint anchorRatio = mul(anchorPrice, 100e16) / reporterPrice;
bool withinAnchor = anchorRatio <= upperBoundAnchorRatio && anchorRatio >= lowerBoundAnchorRatio;

Expand Down Expand Up @@ -217,7 +217,7 @@ contract AnchoredView is SymbolConfiguration {
/// @notice invalidate the reporter, and fall back to using anchor directly in all cases
function invalidate(bytes memory message, bytes memory signature) public {
(string memory decoded_message, ) = abi.decode(message, (string, address));
require(keccak256(abi.encodePacked(decoded_message)) == keccak256(abi.encodePacked("rotate")), "invalid message must be 'rotate'");
require(keccak256(abi.encodePacked(decoded_message)) == keccak256(abi.encodePacked("rotate")), "invalidation message must be 'rotate'");
require(priceData.source(message, signature) == reporter, "invalidation message must come from the reporter");

reporterBreaker = true;
Expand Down
6 changes: 4 additions & 2 deletions contracts/AnchoredView/SymbolConfiguration.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;


Expand All @@ -13,7 +15,7 @@ contract SymbolConfiguration {
address public constant cDaiAnchorKey = address(2);

/// @notice standard amount for the Dollar
uint constant oneDollar = 1e6;
uint public constant oneDollar = 1e6;

// Address of the oracle key (underlying) for cTokens non special keyed tokens
address public immutable cRepAnchorKey;
Expand Down
10 changes: 6 additions & 4 deletions contracts/DelFiPrice.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;

import "./OpenOraclePriceData.sol";
Expand All @@ -15,13 +17,13 @@ contract DelFiPrice is OpenOracleView {
/// @notice The event emitted when new prices are posted but the median price is not updated due to the anchor
event PriceGuarded(string symbol, uint64 median, uint64 anchor);

/// @notice The reporter address whose prices checked against the median for safety
/// @dev The reporter address whose prices checked against the median for safety
address anchor;

/// @notice The highest ratio of the new median price to the anchor price that will still trigger the median price to be updated
/// @dev The highest ratio of the new median price to the anchor price that will still trigger the median price to be updated
uint256 upperBoundAnchorRatio;

/// @notice The lowest ratio of the new median price to the anchor price that will still trigger the median price to be updated
/// @dev The lowest ratio of the new median price to the anchor price that will still trigger the median price to be updated
uint256 lowerBoundAnchorRatio;

/// @notice The mapping of medianized prices per symbol
Expand Down
4 changes: 3 additions & 1 deletion contracts/OpenOracleData.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;

/**
Expand Down
10 changes: 6 additions & 4 deletions contracts/OpenOraclePriceData.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;

import "./OpenOracleData.sol";

Expand All @@ -20,8 +22,8 @@ contract OpenOraclePriceData is OpenOracleData {
}

/**
* @notice The most recent authenticated data from all sources
* @dev This is private because dynamic mapping keys preclude auto-generated getters.
* @dev The most recent authenticated data from all sources.
* This is private because dynamic mapping keys preclude auto-generated getters.
*/
mapping(address => mapping(string => Datum)) private data;

Expand All @@ -41,7 +43,7 @@ contract OpenOraclePriceData is OpenOracleData {

// Only update if newer than stored, according to source
Datum storage prior = data[source][key];
if (timestamp > prior.timestamp && timestamp < block.timestamp + 60 minutes) {
if (timestamp > prior.timestamp && timestamp < block.timestamp + 60 minutes && source != address(0)) {
data[source][key] = Datum(timestamp, value);
emit Write(source, key, timestamp, value);
} else {
Expand Down
4 changes: 3 additions & 1 deletion contracts/OpenOracleView.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pragma solidity ^0.6.6;
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.6.10;

import "./OpenOracleData.sol";

Expand Down
Loading

0 comments on commit d0a0d03

Please sign in to comment.