-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from UMAprotocol/reinis-frp/wip-2
Reinis frp/wip 2
- Loading branch information
Showing
24 changed files
with
504 additions
and
196 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,66 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "./interfaces/AggregatorV3Interface.sol"; | ||
import "./interfaces/IBaseOracleAdapter.sol"; | ||
import "./interfaces/IOevOracle.sol"; | ||
|
||
import "openzeppelin-contracts/contracts/access/Ownable.sol"; | ||
//TODO: potentially make this abstract? | ||
contract OevOracle is IOevOracle { | ||
IBaseOracleAdapter public sourceAdapter; | ||
//TODO: add method to UpdateController to change the sourceAdapter. | ||
|
||
contract OevOracle is AggregatorV3Interface, Ownable { | ||
int256 public override latestAnswer; | ||
uint256 public override latestTimestamp; | ||
uint256 public override latestRound; | ||
//TODO: think about what we'd want the ideal internal and public interfaces to be that are exposed over all destination adapters. | ||
|
||
struct RoundData { | ||
uint80 roundId; | ||
int256 answer; | ||
uint256 startedAt; | ||
uint256 updatedAt; | ||
uint80 answeredInRound; | ||
} | ||
|
||
mapping(uint256 => RoundData) roundData; | ||
|
||
AggregatorV3Interface public baseAggregator; | ||
|
||
address public updater; | ||
int256 public cachedLatestAnswer; // Always 18 decimals. | ||
|
||
uint256 public permissionWindow = 10 minutes; | ||
uint256 public cachedLatestTimestamp; | ||
|
||
constructor(AggregatorV3Interface _baseAggregator, address _updater) { | ||
baseAggregator = _baseAggregator; | ||
updater = _updater; | ||
|
||
updateAnswer(); | ||
constructor(IBaseOracleAdapter _sourceAdapter) { | ||
sourceAdapter = _sourceAdapter; | ||
} | ||
|
||
function updateAnswer() public { | ||
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = | ||
baseAggregator.latestRoundData(); | ||
|
||
require(latestRound != roundId, "No update needed"); | ||
if (msg.sender != updater && msg.sender != owner()) { | ||
require(block.timestamp - updatedAt >= permissionWindow, "In permissioned window"); | ||
} | ||
|
||
latestRound = roundId; | ||
latestAnswer = answer; | ||
latestTimestamp = updatedAt; | ||
roundData[roundId] = RoundData(roundId, answer, startedAt, updatedAt, answeredInRound); | ||
} | ||
require(_canUpdate(cachedLatestAnswer)); | ||
int256 _latestAnswer = sourceAdapter.latestAnswer(); | ||
uint256 _latestTimestamp = sourceAdapter.latestTimestamp(); | ||
|
||
function canUpdateAnswer() public view returns (bool) { | ||
(uint256 roundId,,,,) = baseAggregator.latestRoundData(); | ||
return latestRound != roundId; | ||
} | ||
require(_latestAnswer != cachedLatestAnswer || _latestTimestamp != cachedLatestTimestamp); | ||
|
||
function setUpdater(address _updater) public onlyOwner { | ||
updater = _updater; | ||
} | ||
require(_validateUpdate(_latestAnswer, _latestTimestamp)); | ||
|
||
function setPermissionWindow(uint256 _permissionWindow) public onlyOwner { | ||
permissionWindow = _permissionWindow; | ||
cachedLatestAnswer = _latestAnswer; | ||
cachedLatestTimestamp = _latestTimestamp; | ||
} | ||
|
||
function getRoundData(uint80 _roundId) | ||
external | ||
view | ||
override | ||
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) | ||
{ | ||
return ( | ||
roundData[_roundId].roundId, | ||
roundData[_roundId].answer, | ||
roundData[_roundId].startedAt, | ||
roundData[_roundId].updatedAt, | ||
roundData[_roundId].answeredInRound | ||
); | ||
function rawLatestAnswer() public view override returns (int256) { | ||
if (_canReturnCachedValue(cachedLatestTimestamp)) return cachedLatestAnswer; | ||
return sourceAdapter.latestAnswer(); | ||
} | ||
|
||
function latestRoundData() | ||
external | ||
view | ||
override | ||
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) | ||
{ | ||
return ( | ||
roundData[latestRound].roundId, | ||
roundData[latestRound].answer, | ||
roundData[latestRound].startedAt, | ||
roundData[latestRound].updatedAt, | ||
roundData[latestRound].answeredInRound | ||
); | ||
function rawLatestTimestamp() public view override returns (uint256) { | ||
if (_canReturnCachedValue(cachedLatestTimestamp)) return cachedLatestTimestamp; | ||
return sourceAdapter.latestTimestamp(); | ||
} | ||
|
||
function getAnswer(uint256 roundId) public view override returns (int256) { | ||
return roundData[roundId].answer; | ||
function _canUpdate(int256 _latestAnswer) internal view returns (bool) { | ||
return true; | ||
} | ||
|
||
function getTimestamp(uint256 roundId) public view override returns (uint256) { | ||
return roundData[roundId].updatedAt; | ||
function setSourceOracleAdapter(IBaseOracleAdapter _sourceAdapter) external { | ||
require(_canSetSourceOracleAdapter()); | ||
sourceAdapter = _sourceAdapter; | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return baseAggregator.decimals(); | ||
function _validateUpdate(int256 _latestAnswer, uint256 _latestTimestamp) internal view returns (bool) { | ||
return true; | ||
} | ||
|
||
function description() public view override returns (string memory) { | ||
return baseAggregator.description(); | ||
function _canReturnCachedValue(uint256 _cachedValue) internal view returns (bool) { | ||
return false; | ||
} | ||
|
||
function version() public view override returns (uint256) { | ||
return baseAggregator.version(); | ||
function _canSetSourceOracleAdapter() internal view returns (bool) { | ||
return false; | ||
} | ||
|
||
// TODO: we have no notion of phase or phase ID at this point. Should be pulled from Aggregator. | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pragma solidity 0.8.17; | ||
|
||
import "../controllers/BaseUpdateController.sol"; | ||
import "../interfaces/IBaseOracleAdapter.sol"; | ||
import "../OevOracle.sol"; | ||
|
||
contract BaseDestinationOracleAdapter is BaseUpdateController, OevOracle, IBaseOracleAdapter { | ||
|
||
constructor(IBaseOracleAdapter _sourceAdapter) OevOracle(_sourceAdapter) { | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return 18; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "../interfaces/IBaseOracleAdapter.sol"; | ||
|
||
contract MeanSourceOracleAdapter is IBaseOracleAdapter { | ||
IBaseOracleAdapter[] public sources; | ||
|
||
uint256 public immutable numberOfSources; | ||
|
||
constructor(IBaseOracleAdapter[] memory _sources) { | ||
sources = _sources; | ||
numberOfSources = sources.length; | ||
} | ||
|
||
function latestAnswer() public view override returns (int256) { | ||
int256 sum = 0; | ||
for (uint256 i = 0; i < numberOfSources; i++) { | ||
sum += sources[i].latestAnswer(); | ||
} | ||
return sum / int256(numberOfSources); | ||
} | ||
|
||
function latestTimestamp() public view override returns (uint256) { | ||
uint256 max = 0; | ||
for (uint256 i = 0; i < numberOfSources; i++) { | ||
uint256 timestamp = sources[i].latestTimestamp(); | ||
if (timestamp > max) max = timestamp; | ||
} | ||
return max; | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return 18; | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/adapters/chainlink/ChainlinkDestinationOracleAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "../../controllers/BaseUpdateController.sol"; | ||
import "../../interfaces/IBaseOracleAdapter.sol"; | ||
import "../../interfaces/chainlink/IAggregatorV3.sol"; | ||
import "../../OevOracle.sol"; | ||
|
||
contract ChainlinkDestinationOracleAdapter is | ||
BaseUpdateController, | ||
OevOracle, | ||
IBaseOracleAdapter, | ||
IAggregatorV3 | ||
{ | ||
uint8 public decimals; | ||
|
||
constructor(uint8 _decimals, IBaseOracleAdapter _sourceAdapter) | ||
OevOracle(_sourceAdapter) | ||
{ | ||
decimals = _decimals; | ||
} | ||
|
||
function latestAnswer() public view override returns (int256) { | ||
// TODO: if we never have more than 18 decimals on the output oracle then we can subtract decimals and divide. | ||
return (rawLatestAnswer() * int256(10**uint256(decimals))) / (10**18); | ||
} | ||
|
||
function latestTimestamp() public view override returns (uint256) { | ||
return rawLatestTimestamp(); | ||
} | ||
|
||
function latestRoundData() | ||
external | ||
view | ||
returns ( | ||
uint80 roundId, | ||
int256 answer, | ||
uint256 startedAt, | ||
uint256 updatedAt, | ||
uint80 answeredInRound | ||
) | ||
{ | ||
return (0, latestAnswer(), 0, 0, 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "../../interfaces/IBaseOracleAdapter.sol"; | ||
import "../../interfaces/chainlink/IAggregatorV3.sol"; | ||
|
||
contract ChainlinkSourceOracleAdapter is IBaseOracleAdapter { | ||
IAggregatorV3 public source; | ||
|
||
constructor(address _source) { | ||
source = IAggregatorV3(_source); | ||
} | ||
|
||
function latestAnswer() public view override returns (int256) { | ||
return (source.latestAnswer() * 10 ** 18) / int256(10 ** decimals()); | ||
} | ||
|
||
function latestTimestamp() public view override returns (uint256) { | ||
return source.latestTimestamp(); | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return source.decimals(); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
src/adapters/compound/UniswapAnchoredViewDestinationOracleAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
pragma solidity 0.8.17; | ||
|
||
import "openzeppelin-contracts/contracts/access/Ownable.sol"; | ||
|
||
import "../../interfaces/compound/IUniswapAnchoredView.sol"; | ||
import "../../interfaces/IOevOracle.sol"; | ||
import "../../interfaces/compound/ICToken.sol"; | ||
|
||
contract UniswapAnchoredViewDestinationOracleAdapter is Ownable, IUniswapAnchoredView { | ||
mapping(address => address) public cTokenToOevOracles; | ||
mapping(address => uint8) public cTokenToDecimals; | ||
|
||
constructor () Ownable() { | ||
} | ||
|
||
function setOevOracle(address cToken, address oevOracle) public onlyOwner { | ||
cTokenToOevOracles[cToken] = oevOracle; | ||
cTokenToDecimals[cToken] = ICToken(cToken).decimals(); | ||
} | ||
|
||
function getUnderlyingPrice(address cToken) external view returns (uint256) { | ||
uint256 rawPrice = IOevOracle(cTokenToOevOracles[cToken]).rawLatestAnswer(); | ||
return rawPrice * (10 ** (36 - cTokenToDecimals[cToken])) / (10 ** 18); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/adapters/compound/UniswapAnchoredViewSourceOracleAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "../../interfaces/IBaseOracleAdapter.sol"; | ||
import "../../interfaces/chainlink/IAggregatorV3.sol"; | ||
import "../../interfaces/compound/IUniswapAnchoredView.sol"; | ||
import "../../interfaces/compound/ICToken.sol"; | ||
import "../../interfaces/compound/IValidatorProxy.sol"; | ||
|
||
contract UniswapAnchoredViewSourceOracleAdapter is IBaseOracleAdapter, IUniswapAnchoredView { | ||
IUniswapAnchoredView public source; | ||
|
||
ICToken public cToken; | ||
|
||
uint8 public decimals; | ||
|
||
IAggregatorV3 public aggregator; | ||
|
||
constructor(IUniswapAnchoredView _source, address _cToken) { | ||
source = _source; | ||
cToken = ICToken(_cToken); | ||
decimals = cToken.decimals(); | ||
|
||
TokenConfig memory tokenConfig = source.getTokenConfigByCToken(address(cToken)); | ||
// TODO: make sure this does not change over time | ||
(address current,,) = IValidatorProxy(tokenConfig.reporter).getAggregators(); | ||
aggregator = IAggregatorV3(current); | ||
} | ||
|
||
function latestAnswer() public view override returns (int256) { | ||
// Price is scaled with (36 - decimals) | ||
uint256 sourcePrice = source.getUnderlyingPrice(address(cToken)); | ||
return int256(sourcePrice * (10 ** 18) / (10 ** (36 - decimals))); // TODO: merge power math to avoid overflow | ||
} | ||
|
||
function latestTimestamp() public view override returns (uint256) { | ||
return aggregator.latestTimestamp(); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/adapters/makerdao/ChronicleDestinationOracleAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.17; | ||
|
||
import "../../controllers/BaseUpdateController.sol"; | ||
import "../../interfaces/IBaseOracleAdapter.sol"; | ||
import "../../interfaces/chronicle/IMedian.sol"; | ||
import "../../OevOracle.sol"; | ||
|
||
contract ChronicleDestinationOracleAdapter is BaseUpdateController, OevOracle, IMedian { | ||
uint8 public decimals; | ||
|
||
constructor(uint8 _decimals, IBaseOracleAdapter _sourceAdapter) OevOracle(_sourceAdapter) { | ||
decimals = _decimals; | ||
} | ||
|
||
function read() public view override returns (uint256) { | ||
// TODO: if we never have more than 18 decimals on the output oracle then we can subtract decimals and divide. | ||
return (uint256(rawLatestAnswer()) * (10 ** decimals)) / (10 ** 18); | ||
} | ||
|
||
// TODO: might need to add toll functionality to replicate consumer authorization as in MakerDao. | ||
function peek() public view override returns (uint256, bool) { | ||
uint256 val = read(); | ||
return (val, val > 0); | ||
} | ||
|
||
function age() public view override returns (uint32) { | ||
return uint32(rawLatestTimestamp()); | ||
} | ||
|
||
function bar() public view override returns (uint256) { | ||
return uint256(decimals); | ||
} | ||
} |
Oops, something went wrong.