Skip to content

Commit

Permalink
Ensure assetToUsd for oracles
Browse files Browse the repository at this point in the history
Signed-off-by: 34x4p08 <[email protected]>
  • Loading branch information
chainpioneer committed Apr 2, 2021
1 parent 73a8215 commit 3775077
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 100 deletions.
10 changes: 5 additions & 5 deletions contracts/ParametersBatchUpdater.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pragma abicoder v2;

import "./VaultParameters.sol";
import "./interfaces/IVaultManagerParameters.sol";
import "./interfaces/IBearingAssetOracleSimple.sol";
import "./interfaces/IBearingAssetOracle.sol";
import "./interfaces/IOracleRegistry.sol";
import "./interfaces/ICollateralRegistry.sol";
import "./interfaces/IVault.sol";
Expand Down Expand Up @@ -154,10 +154,10 @@ contract ParametersBatchUpdater is Auth {
}
}

function setOracleTypesInRegistry(uint[] calldata oracleTypes, address[] calldata oracles) public onlyManager {
require(oracleTypes.length == oracles.length, "Unit Protocol: ARGUMENTS_LENGTH_MISMATCH");
function setOracleTypesInRegistry(uint[] calldata oracleTypes, address[] calldata oracles, bool[] calldata quoteInEthSupported) public onlyManager {
require(oracleTypes.length == oracles.length && quoteInEthSupported.length == oracles.length, "Unit Protocol: ARGUMENTS_LENGTH_MISMATCH");
for (uint i = 0; i < oracleTypes.length; i++) {
oracleRegistry.setOracle(oracleTypes[i], oracles[i]);
oracleRegistry.setOracle(oracleTypes[i], oracles[i], quoteInEthSupported[i]);
}
}

Expand All @@ -178,7 +178,7 @@ contract ParametersBatchUpdater is Auth {
function setUnderlyings(address[] calldata bearings, address[] calldata underlyings) public onlyManager {
require(bearings.length == underlyings.length, "Unit Protocol: ARGUMENTS_LENGTH_MISMATCH");
for (uint i = 0; i < bearings.length; i++) {
IBearingAssetOracleSimple(oracleRegistry.oracleByType(BEARING_ASSET_ORACLE_TYPE)).setUnderlying(bearings[i], underlyings[i]);
IBearingAssetOracle(oracleRegistry.oracleByType(BEARING_ASSET_ORACLE_TYPE)).setUnderlying(bearings[i], underlyings[i]);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
interface IBearingAssetOracleSimple {
interface IBearingAssetOracle {
function assetToEth ( address bearing, uint256 amount ) external view returns ( uint256 );
function assetToUsd ( address bearing, uint256 amount ) external view returns ( uint256 );
function bearingToUnderlying ( address bearing, uint256 amount ) external view returns ( address, uint256 );
function oracleRegistry ( ) external view returns ( address );
Expand Down
7 changes: 7 additions & 0 deletions contracts/interfaces/IOracleEth.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
interface IOracleEth {

// returns Q112-encoded value
function assetToEth(address asset, uint amount) external view returns (uint);

// returns the value "as is"
function ethToUsd(uint amount) external view returns (uint);

// returns the value "as is"
function usdToEth(uint amount) external view returns (uint);
}
8 changes: 8 additions & 0 deletions contracts/interfaces/IOracleForAsset.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface IOracleForAsset {

// returns Q112-encoded value
function assetToUsd(address asset, uint amount) external view returns (uint);

// returns Q112-encoded value
function assetToEth(address asset, uint amount) external view returns (uint);
}
19 changes: 16 additions & 3 deletions contracts/interfaces/IOracleRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
pragma abicoder v2;


interface IOracleRegistry {
function getOracles ( ) external view returns ( address[] memory oracles );

struct Oracle {
uint oracleType;
address oracleAddress;
bool quoteInEth;
}

function WETH ( ) external view returns ( address );
function getOracles ( ) external view returns ( Oracle[] memory foundOracles );
function maxOracleType ( ) external view returns ( uint256 );
function oracleByAsset ( address asset ) external view returns ( address );
function oracleByType ( uint256 ) external view returns ( address );
function oracleTypeByAsset ( address ) external view returns ( uint256 );
function setOracle ( uint256 oracleType, address oracle ) external;
function oracleTypeByOracle ( address ) external view returns ( uint256 );
function quoteInEthSupportByOracle ( address oracle ) external view returns ( bool );
function quoteInEthSupported ( uint256 ) external view returns ( bool );
function setOracle ( uint256 oracleType, address oracle, bool _quoteInEthSupported ) external;
function setOracleTypeToAsset ( address asset, uint256 oracleType ) external;
function setOracleTypeToAssets ( address[] memory assets, uint256 oracleType ) external;
function typeByOracle ( address ) external view returns ( uint256 );
function vaultParameters ( ) external view returns ( address );
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
interface IOracleSimple {
interface IOracleUsd {

// returns Q112-encoded value
function assetToUsd(address asset, uint amount) external view returns (uint);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
*/
pragma solidity 0.7.6;

import "../interfaces/IOracleSimple.sol";
import "../interfaces/IOracleUsd.sol";
import "../helpers/ERC20Like.sol";
import "../VaultParameters.sol";
import "../interfaces/IOracleRegistry.sol";
import "../interfaces/IOracleForAsset.sol";
import "../interfaces/IOracleEth.sol";

/**
* @title BearingAssetOracleSimple
* @dev Wrapper to quote bearing assets like xSUSHI
**/
contract BearingAssetOracleSimple is IOracleSimple, Auth {
contract BearingAssetOracle is IOracleForAsset, Auth {

IOracleRegistry public immutable oracleRegistry;

Expand All @@ -36,10 +38,22 @@ contract BearingAssetOracleSimple is IOracleSimple, Auth {
function assetToUsd(address bearing, uint amount) public override view returns (uint) {
if (amount == 0) return 0;
(address underlying, uint underlyingAmount) = bearingToUnderlying(bearing, amount);
IOracleSimple _oracleForUnderlying = IOracleSimple(oracleRegistry.oracleByAsset(underlying));
IOracleUsd _oracleForUnderlying = IOracleUsd(oracleRegistry.oracleByAsset(underlying));
return _oracleForUnderlying.assetToUsd(underlying, underlyingAmount);
}

// returns Q112-encoded value
function assetToEth(address bearing, uint amount) public override view returns (uint) {
if (amount == 0) return 0;
(address underlying, uint underlyingAmount) = bearingToUnderlying(bearing, amount);
IOracleForAsset _oracleForUnderlying = IOracleForAsset(oracleRegistry.oracleByAsset(underlying));
if (oracleRegistry.quoteInEthSupportByOracle(address(_oracleForUnderlying))) {
return _oracleForUnderlying.assetToEth(underlying, underlyingAmount);
}
uint usdValue_q112 = _oracleForUnderlying.assetToUsd(underlying, underlyingAmount);
return IOracleEth(oracleRegistry.oracleByAsset(oracleRegistry.WETH())).usdToEth(usdValue_q112);
}

function bearingToUnderlying(address bearing, uint amount) public view returns (address, uint) {
address _underlying = underlyings[bearing];
require(_underlying != address(0), "Unit Protocol: UNDEFINED_UNDERLYING");
Expand Down
8 changes: 4 additions & 4 deletions contracts/oracles/ChainlinkedOracleMainAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pragma solidity 0.7.6;
import "../helpers/SafeMath.sol";
import "../VaultParameters.sol";
import "../interfaces/IAggregator.sol";
import "../interfaces/IOracleSimple.sol";
import "../interfaces/IOracleUsd.sol";
import "../interfaces/IOracleEth.sol";

interface ERC20 {
Expand All @@ -19,7 +19,7 @@ interface ERC20 {
* @title ChainlinkedOracleMainAsset
* @dev Calculates the USD price of desired tokens
**/
contract ChainlinkedOracleMainAsset is IOracleSimple, IOracleEth, Auth {
contract ChainlinkedOracleMainAsset is IOracleUsd, IOracleEth, Auth {
using SafeMath for uint;

mapping (address => address) public usdAggregators;
Expand Down Expand Up @@ -129,7 +129,7 @@ contract ChainlinkedOracleMainAsset is IOracleSimple, IOracleEth, Auth {
if (address(agg) == address (0)) {
// check for usd aggregator
require(usdAggregators[asset] != address (0), "Unit Protocol: AGGREGATOR_DOES_NOT_EXIST");
return _usdToEth(_assetToUsd(asset, amount));
return usdToEth(_assetToUsd(asset, amount));
}

(, int256 answer, , uint256 updatedAt, ) = agg.latestRoundData();
Expand All @@ -154,7 +154,7 @@ contract ChainlinkedOracleMainAsset is IOracleSimple, IOracleEth, Auth {
return ethAmount.mul(uint(answer)).div(10 ** agg.decimals());
}

function _usdToEth(uint _usdAmount) internal view returns (uint) {
function usdToEth(uint _usdAmount) public override view returns (uint) {
IAggregator agg = IAggregator(usdAggregators[WETH]);
(, int256 answer, , uint256 updatedAt, ) = agg.latestRoundData();
require(updatedAt > block.timestamp - 6 hours, "Unit Protocol: STALE_CHAINLINK_PRICE");
Expand Down
37 changes: 23 additions & 14 deletions contracts/oracles/CurveLPOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
*/
pragma solidity 0.7.6;

import "./OracleSimple.sol";
import "../interfaces/IOracleUsd.sol";
import "../interfaces/IOracleEth.sol";
import "../helpers/ERC20Like.sol";
import "./OracleRegistry.sol";
import "../interfaces/IOracleForAsset.sol";
import "../interfaces/IOracleRegistry.sol";

interface CurveProvider {
function get_registry() external view returns (address);
Expand All @@ -27,24 +30,24 @@ interface CurvePool {
* @title CurveLPOracle
* @dev Oracle to quote curve LP tokens
**/
contract CurveLPOracle is OracleSimple {
contract CurveLPOracle is IOracleForAsset {

uint public constant Q112 = 2 ** 112;
uint public constant PRECISION = 1e18;

// CurveProvider contract
CurveProvider public immutable curveProvider;
// ChainlinkedOracle contract
ChainlinkedOracleSimple public immutable chainlinkedOracle;
IOracleRegistry public immutable oracleRegistry;

/**
* @param _curveProvider The address of the Curve Provider. Mainnet: 0x0000000022D53366457F9d5E68Ec105046FC4383
* @param _chainlinkedOracle The address of the Chainlinked Oracle
* @param _oracleRegistry The address of the OracleRegistry contract
**/
constructor(address _curveProvider, address _chainlinkedOracle) {
require(_curveProvider != address(0) && _chainlinkedOracle != address(0), "Unit Protocol: ZERO_ADDRESS");
constructor(address _curveProvider, address _oracleRegistry) {
require(_curveProvider != address(0) && _oracleRegistry != address(0), "Unit Protocol: ZERO_ADDRESS");
curveProvider = CurveProvider(_curveProvider);
chainlinkedOracle = ChainlinkedOracleSimple(_chainlinkedOracle);
oracleRegistry = IOracleRegistry(_oracleRegistry);
}

// returns Q112-encoded value
Expand All @@ -58,20 +61,26 @@ contract CurveLPOracle is OracleSimple {
uint coinsCount = cR.get_n_coins(address(cP))[0];
require(coinsCount != 0, "Unit Protocol: CURVE_INCORRECT_COINS_COUNT");

uint minEthCoinPrice_q112;
uint minCoinPrice_q112;

for (uint i = 0; i < coinsCount; i++) {
uint ethCoinPrice_q112 = chainlinkedOracle.assetToEth(cP.coins(i), 1 ether);
if (i == 0 || ethCoinPrice_q112 < minEthCoinPrice_q112) {
minEthCoinPrice_q112 = ethCoinPrice_q112;
address _coin = cP.coins(i);
uint _coinPrice_q112 = IOracleUsd(oracleRegistry.oracleByAsset(_coin)).assetToUsd(_coin, 1);
if (i == 0 || _coinPrice_q112 < minCoinPrice_q112) {
minCoinPrice_q112 = _coinPrice_q112;
}
}

uint minUsdCoinPrice_q112 = chainlinkedOracle.ethToUsd(minEthCoinPrice_q112) / 1 ether;

uint price_q112 = cP.get_virtual_price() * minUsdCoinPrice_q112 / PRECISION;
uint price_q112 = cP.get_virtual_price() * minCoinPrice_q112 / PRECISION;

return amount * price_q112;
}

// returns Q112-encoded value
function assetToEth(address asset, uint amount) public override view returns (uint) {
if (amount == 0) return 0;
uint usdValue_q112 = assetToUsd(asset, amount);
return IOracleEth(oracleRegistry.oracleByAsset(oracleRegistry.WETH())).usdToEth(usdValue_q112);
}

}
14 changes: 11 additions & 3 deletions contracts/oracles/OraclePoolToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ pragma solidity 0.7.6;
import "../helpers/SafeMath.sol";
import "../helpers/IUniswapV2PairFull.sol";
import "../interfaces/IOracleEth.sol";
import "../interfaces/IOracleSimple.sol";
import "../interfaces/IOracleUsd.sol";
import "../interfaces/IOracleRegistry.sol";


/**
* @title OraclePoolToken
* @dev Calculates the USD price of Uniswap LP tokens
**/
contract OraclePoolToken is IOracleSimple {
contract OraclePoolToken is IOracleUsd {
using SafeMath for uint;

IOracleRegistry public immutable oracleRegistry;
Expand Down Expand Up @@ -57,7 +57,15 @@ contract OraclePoolToken is IOracleSimple {
}

// average price of 1 token in ETH
uint eAvg = IOracleEth(oracleRegistry.oracleByAsset(underlyingAsset)).assetToEth(underlyingAsset, 1);
uint eAvg;

address oracle = oracleRegistry.oracleByAsset(underlyingAsset);

if (oracleRegistry.quoteInEthSupportByOracle(oracle)) {
eAvg = IOracleEth(oracle).assetToEth(underlyingAsset, 1);
} else {
IOracleEth(oracleRegistry.oracleByAsset(oracleRegistry.WETH())).usdToEth(IOracleUsd(oracle).assetToUsd(underlyingAsset, 1));
}

(uint112 _reserve0, uint112 _reserve1,) = pair.getReserves();
uint aPool; // current asset pool
Expand Down
57 changes: 33 additions & 24 deletions contracts/oracles/OracleRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,40 @@
Copyright 2020 Unit Protocol: Artem Zakharov ([email protected]).
*/
pragma solidity 0.7.6;
pragma abicoder v2;

import "../VaultParameters.sol";

contract OracleRegistry is Auth {

struct Oracle {
uint oracleType;
address oracleAddress;
bool quoteInEth;
}

uint public maxOracleType;

// map token to oracle address
address public immutable WETH;

// map asset to oracle type ID
mapping(address => uint) public oracleTypeByAsset;

// map oracle ID to oracle address
// map oracle type ID to oracle address
mapping(uint => address) public oracleByType;

// map oracle address to oracle ID
mapping(address => uint) public typeByOracle;
// whether quote in ETH supported for an oracle type ID
mapping(uint => bool) public quoteInEthSupported;

// map oracle address to oracle type ID
mapping(address => uint) public oracleTypeByOracle;

event AssetOracle(address indexed asset, uint indexed oracleType);
event OracleType(uint indexed oracleType, address indexed oracle);
event OracleType(uint indexed oracleType, address indexed oracle, bool quoteInEthSupported);

constructor(address vaultParameters) Auth(vaultParameters) {
require(vaultParameters != address(0), "Unit Protocol: ZERO_ADDRESS");
constructor(address vaultParameters, address _weth) Auth(vaultParameters) {
require(vaultParameters != address(0) && _weth != address(0), "Unit Protocol: ZERO_ADDRESS");
WETH = _weth;
}

function setOracleTypeToAsset(address asset, uint oracleType) public onlyManager {
Expand All @@ -33,17 +46,18 @@ contract OracleRegistry is Auth {
emit AssetOracle(asset, oracleType);
}

function setOracle(uint oracleType, address oracle) public onlyManager {
function setOracle(uint oracleType, address oracle, bool _quoteInEthSupported) public onlyManager {
require(oracleType != 0, "Unit Protocol: INVALID_ARGS");

if (oracleType > maxOracleType) {
maxOracleType = oracleType;
}

oracleByType[oracleType] = oracle;
typeByOracle[oracle] = oracleType;

emit OracleType(oracleType, oracle);
oracleTypeByOracle[oracle] = oracleType;
quoteInEthSupported[oracleType] = _quoteInEthSupported;

emit OracleType(oracleType, oracle, _quoteInEthSupported);
}

function setOracleTypeToAssets(address[] calldata assets, uint oracleType) public onlyManager {
Expand All @@ -56,26 +70,21 @@ contract OracleRegistry is Auth {
}
}

function getOracles() external view returns (address[] memory oracles) {
function getOracles() external view returns (Oracle[] memory foundOracles) {

// Memory arrays can't be reallocated so we'll overprovision
address[] memory foundOracles = new address[](maxOracleType - 1);
uint actualOraclesCount = 0;
foundOracles = new Oracle[](maxOracleType);

for (uint _type = 1; _type <= maxOracleType; ++_type) {
if (oracleByType[_type] != address(0)) {
foundOracles[actualOraclesCount++] = oracleByType[_type];
}
}

oracles = new address[](actualOraclesCount);
for (uint i = 0; i < actualOraclesCount; ++i) {
oracles[i] = foundOracles[i];
for (uint _type = 0; _type < maxOracleType; ++_type) {
foundOracles[_type] = Oracle(_type, oracleByType[_type], quoteInEthSupported[_type]);
}
}

function oracleByAsset(address asset) external view returns (address) {
return oracleByType[oracleTypeByAsset[asset]];
}

function quoteInEthSupportByOracle(address oracle) external view returns (bool) {
return quoteInEthSupported[oracleTypeByOracle[oracle]];
}

}
Loading

0 comments on commit 3775077

Please sign in to comment.