-
Notifications
You must be signed in to change notification settings - Fork 135
Oracle Factory #1222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Oracle Factory #1222
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
d7e68b0
tweak to also support chainlink interface
tbrent 7713c03
fix linting.
pmckelvy1 eba6525
add natspec to exchange rate oracle
tbrent 46d0c81
Merge remote-tracking branch 'origin/tweak-oracle-factory' into oracl…
akshatmittal 3f26ec2
Extended interface
akshatmittal 7bd40ab
very very wip
akshatmittal 05bf754
Add Yearn Curve Oracle
akshatmittal 83d32b9
nits
akshatmittal 8de027b
Merge branch '4.0.0' into oracle-factory
akshatmittal 62a1968
Merge remote-tracking branch 'origin/4.0.0' into oracle-factory
akshatmittal 47bf2ef
Update some oracles
akshatmittal e455eba
Lint
akshatmittal 6c566fe
Update comments
akshatmittal d2b5b20
Damn you star
akshatmittal 65058c4
Words
akshatmittal 6e7b28f
Oracle factory review (#1259)
tbrent f371dd2
test OracleFactory
tbrent 0a28265
workflow
tbrent 19b4eb5
workflow
tbrent 2c5d2c2
whoops, ETH+
tbrent 031bc6c
Merge branch '4.0.0' into oracle-factory
akshatmittal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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,101 @@ | ||
| // SPDX-License-Identifier: BlueOak-1.0.0 | ||
| pragma solidity 0.8.19; | ||
|
|
||
| import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | ||
| import { IExchangeRateOracle } from "../exchange-rate/IExchangeRateOracle.sol"; | ||
|
|
||
| interface ICurveStableSwapNG { | ||
| function coins(uint256 i) external view returns (address); | ||
|
|
||
| function get_virtual_price() external view returns (uint256); | ||
|
|
||
| function stored_rates() external view returns (uint256[] memory); | ||
| } | ||
|
|
||
| /** | ||
| * @title CurveOracle | ||
| * @notice An immutable Exchange Rate Oracle for a StableSwapNG Curve LP Token, | ||
| * with one or more appreciating assets. Only for 2-asset Curve LP Tokens. | ||
| * @dev Does not account for native asset appreciation, only accounts for the | ||
| * appreciation in the Curve LP via trading volume. | ||
| * | ||
| * The oracles specified for the pool MUST be for the base unit, for example | ||
| * if the paired token is sDAI, you'd specify the oracle for DAI/USD. | ||
| */ | ||
| contract CurveOracle { | ||
| enum OracleType { | ||
| STORED, | ||
| STATIC, | ||
| RTOKEN, | ||
| CHAINLINK | ||
| } | ||
|
|
||
| struct OracleConfig { | ||
| OracleType oracleType; | ||
| address rateProvider; | ||
| uint256 staticValue; | ||
| uint256 timeout; | ||
| } | ||
|
|
||
| error BadOracleValue(); | ||
| error InvalidOracleType(); | ||
|
|
||
| ICurveStableSwapNG public immutable curvePool; | ||
| OracleConfig public oracleConfig0; | ||
| OracleConfig public oracleConfig1; | ||
|
|
||
| constructor( | ||
| address _curvePool, | ||
| OracleConfig memory _oracleConfig0, | ||
| OracleConfig memory _oracleConfig1 | ||
| ) { | ||
| curvePool = ICurveStableSwapNG(_curvePool); | ||
| oracleConfig0 = _oracleConfig0; | ||
| oracleConfig1 = _oracleConfig1; | ||
| } | ||
|
|
||
| function _getTokenPrice(uint256 tokenId) internal view virtual returns (uint256) { | ||
| OracleConfig memory oracleConfig = tokenId == 0 ? oracleConfig0 : oracleConfig1; | ||
| OracleType oracleType = oracleConfig.oracleType; | ||
|
|
||
| if (oracleType == OracleType.STORED) { | ||
| return curvePool.stored_rates()[tokenId]; | ||
| } else if (oracleType == OracleType.STATIC) { | ||
| return oracleConfig.staticValue; | ||
| } else if (oracleType == OracleType.RTOKEN) { | ||
| return IExchangeRateOracle(oracleConfig.rateProvider).exchangeRate(); | ||
| } else if (oracleType == OracleType.CHAINLINK) { | ||
| AggregatorV3Interface oracle = AggregatorV3Interface(oracleConfig.rateProvider); | ||
| uint8 decimals = oracle.decimals(); | ||
| (, int256 price, , uint256 updateTime, ) = oracle.latestRoundData(); | ||
|
|
||
| if (price < 0) { | ||
| revert BadOracleValue(); | ||
| } | ||
|
|
||
| if (block.timestamp - updateTime > oracleConfig.timeout) { | ||
| revert BadOracleValue(); | ||
| } | ||
|
|
||
| if (decimals == 18) { | ||
| return uint256(price); | ||
| } else if (decimals < 18) { | ||
| return uint256(price) * (10**(18 - decimals)); | ||
| } else { | ||
| return uint256(price) / (10**(decimals - 18)); | ||
| } | ||
| } | ||
|
|
||
| revert InvalidOracleType(); | ||
| } | ||
|
|
||
| function getPrice() public view virtual returns (uint256) { | ||
| uint256 token0Price = _getTokenPrice(0); | ||
| uint256 token1Price = _getTokenPrice(1); | ||
|
|
||
| uint256 minPrice = token0Price < token1Price ? token0Price : token1Price; | ||
| uint256 virtualPrice = curvePool.get_virtual_price(); | ||
|
|
||
| return (virtualPrice * minPrice) / 1e18; | ||
| } | ||
| } | ||
74 changes: 74 additions & 0 deletions
74
contracts/facade/oracles/exchange-rate/ExchangeRateOracle.sol
This file contains hidden or 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,74 @@ | ||
| // SPDX-License-Identifier: BlueOak-1.0.0 | ||
| pragma solidity 0.8.19; | ||
|
|
||
| import { FIX_ONE, divuu } from "../../../libraries/Fixed.sol"; | ||
| import { IExchangeRateOracle } from "./IExchangeRateOracle.sol"; | ||
|
|
||
| interface IMinimalRToken { | ||
| function basketsNeeded() external view returns (uint192); | ||
|
|
||
| function totalSupply() external view returns (uint256); | ||
| } | ||
|
|
||
| /** | ||
| * @title ExchangeRateOracle | ||
| * @notice An immutable Exchange Rate Oracle for an RToken | ||
| * | ||
| * ::Warning:: In the event of an RToken taking a loss in excess of the StRSR overcollateralization | ||
| * layer, the devaluation will not be reflected until the RToken is done trading. This causes | ||
| * the exchange rate to be too high during the rebalancing phase. If the exchange rate is relied | ||
| * upon naively, then it could be misleading. | ||
| * | ||
| * As a consumer of this oracle, you may want to guard against this case by monitoring: | ||
| * `rToken.status() == 0 && rToken.fullyCollateralized()` | ||
| * | ||
| * However, note that `fullyCollateralized()` is extremely gas-costly. We recommend executing | ||
| * the function off-chain. `status()` is cheap and more reasonable to be called on-chain. | ||
| */ | ||
| contract ExchangeRateOracle is IExchangeRateOracle { | ||
| error MissingRToken(); | ||
|
|
||
| address public immutable rToken; | ||
|
|
||
| constructor(address _rToken) { | ||
| // allow address(0) | ||
| rToken = _rToken; | ||
| } | ||
|
|
||
| function exchangeRate() public view returns (uint256) { | ||
| if (rToken == address(0)) { | ||
| revert MissingRToken(); | ||
| } | ||
|
|
||
| uint256 supply = IMinimalRToken(rToken).totalSupply(); | ||
| if (supply == 0) { | ||
| return FIX_ONE; | ||
| } | ||
|
|
||
| return divuu(uint256(IMinimalRToken(rToken).basketsNeeded()), supply); | ||
| } | ||
|
|
||
| function latestRoundData() | ||
| external | ||
| view | ||
| returns ( | ||
| uint80 roundId, | ||
| int256 answer, | ||
| uint256 startedAt, | ||
| uint256 updatedAt, | ||
| uint80 answeredInRound | ||
| ) | ||
| { | ||
| return ( | ||
| uint80(block.timestamp), | ||
|
akshatmittal marked this conversation as resolved.
Outdated
|
||
| int256(exchangeRate()), | ||
| block.timestamp - 1, | ||
| block.timestamp, | ||
| uint80(block.timestamp) | ||
| ); | ||
| } | ||
|
|
||
| function decimals() external pure returns (uint8) { | ||
| return 18; // RToken is always 18 decimals | ||
| } | ||
| } | ||
36 changes: 36 additions & 0 deletions
36
contracts/facade/oracles/exchange-rate/ExchangeRateOracleFactory.sol
This file contains hidden or 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: BlueOak-1.0.0 | ||
| pragma solidity 0.8.19; | ||
|
|
||
| import { ExchangeRateOracle } from "./ExchangeRateOracle.sol"; | ||
|
|
||
| /** | ||
| * @title ExchangeRateOracleFactory | ||
| * @notice An immutable factory for RToken Exchange Rate Oracles | ||
| */ | ||
| contract ExchangeRateOracleFactory { | ||
| error OracleAlreadyDeployed(address oracle); | ||
|
|
||
| event OracleDeployed(address indexed rToken, address indexed oracle); | ||
|
|
||
| // {rtoken} => {oracle} | ||
| mapping(address => ExchangeRateOracle) public oracles; | ||
|
|
||
| function deployOracle(address rToken) external returns (address) { | ||
| if (address(oracles[rToken]) != address(0)) { | ||
| revert OracleAlreadyDeployed(address(oracles[rToken])); | ||
| } | ||
|
|
||
| ExchangeRateOracle oracle = new ExchangeRateOracle(rToken); | ||
|
|
||
| if (rToken != address(0)) { | ||
| oracle.exchangeRate(); | ||
| oracle.latestRoundData(); | ||
| oracle.decimals(); | ||
| } | ||
|
|
||
| oracles[rToken] = oracle; | ||
| emit OracleDeployed(address(rToken), address(oracle)); | ||
|
|
||
| return address(oracle); | ||
| } | ||
| } |
6 changes: 6 additions & 0 deletions
6
contracts/facade/oracles/exchange-rate/IExchangeRateOracle.sol
This file contains hidden or 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,6 @@ | ||
| // SPDX-License-Identifier: BlueOak-1.0.0 | ||
| pragma solidity 0.8.19; | ||
|
|
||
| interface IExchangeRateOracle { | ||
| function exchangeRate() external view returns (uint256); | ||
| } |
32 changes: 32 additions & 0 deletions
32
contracts/facade/oracles/yearn-curve-oracle/YearnCurveOracle.sol
This file contains hidden or 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,32 @@ | ||
| // SPDX-License-Identifier: BlueOak-1.0.0 | ||
| pragma solidity 0.8.19; | ||
|
|
||
| import { CurveOracle } from "../curve-oracle/CurveOracle.sol"; | ||
|
|
||
| interface YearnVault { | ||
| function pricePerShare() external view returns (uint256); | ||
| } | ||
|
|
||
| /** | ||
| * @title YearnCurveOracle | ||
| * @notice An immutable Exchange Rate Oracle for a Yearn Vault containing a Curve LP Token, | ||
| * with one or more appreciating assets. Only for 2-asset Curve LP Tokens. | ||
| */ | ||
| contract YearnCurveOracle is CurveOracle { | ||
| YearnVault public immutable yearnVault; | ||
|
|
||
| constructor( | ||
| address _yearnVault, | ||
| address _curvePool, | ||
| OracleConfig memory _oracleConfig0, | ||
| OracleConfig memory _oracleConfig1 | ||
| ) CurveOracle(_curvePool, _oracleConfig0, _oracleConfig1) { | ||
| yearnVault = YearnVault(_yearnVault); | ||
| } | ||
|
|
||
| function getPrice() public view virtual override returns (uint256) { | ||
| uint256 pricePerShare = yearnVault.pricePerShare(); | ||
|
|
||
| return (CurveOracle.getPrice() * pricePerShare) / 1e18; | ||
| } | ||
| } |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.