Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 62 additions & 34 deletions contracts/contracts/oracle/OracleRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ abstract contract OracleRouterBase is IOracle {
* @param asset address of the asset
* @return uint256 USD price of 1 of the asset, in 8 decimal fixed
*/
function price(address asset) external view override returns (uint256) {
function price(address asset)
external
view
virtual
override
returns (uint256)
{
address _feed = feed(asset);
if (_feed == FIXED_PRICE) {
return 1e8;
}
require(_feed != address(0), "Asset not available");
require(_feed != FIXED_PRICE, "Fixed price feeds not supported");
(, int256 _iprice, , , ) = AggregatorV3Interface(_feed)
.latestRoundData();
uint256 _price = uint256(_iprice);
Expand All @@ -54,50 +58,74 @@ contract OracleRouter is OracleRouterBase {
* @param asset address of the asset
*/
function feed(address asset) internal pure override returns (address) {
if (asset == address(0x6B175474E89094C44Da98b954EedeAC495271d0F)) {
if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {
// Chainlink: DAI/USD
return address(0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9);
} else if (
asset == address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
) {
return 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;
} else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {
// Chainlink: USDC/USD
return address(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6);
} else if (
asset == address(0xdAC17F958D2ee523a2206206994597C13D831ec7)
) {
return 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;
} else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {
// Chainlink: USDT/USD
return address(0x3E7d1eAB13ad0104d2750B8863b489D65364e32D);
} else if (
asset == address(0xc00e94Cb662C3520282E6f5717214004A7f26888)
) {
return 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;
} else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {
// Chainlink: COMP/USD
return address(0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5);
} else if (
asset == address(0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9)
) {
return 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;
} else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {
// Chainlink: AAVE/USD
return address(0x547a514d5e3769680Ce22B2361c10Ea13619e8a9);
} else if (
asset == address(0xD533a949740bb3306d119CC777fa900bA034cd52)
) {
return 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;
} else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {
// Chainlink: CRV/USD
return address(0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f);
} else if (
asset == address(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B)
) {
return 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;
} else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {
// Chainlink: CVX/USD
return address(0xd962fC30A72A84cE50161031391756Bf2876Af5D);
} else if (
asset == address(0x5E8422345238F34275888049021821E8E08CAa1f)
) {
return 0xd962fC30A72A84cE50161031391756Bf2876Af5D;
} else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {
// Chainlink: rETH/ETH
return 0x536218f9E9Eb48863970252233c8F271f554C2d0;
} else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {
// Chainlink: cbETH/ETH
return 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;
} else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {
// Chainlink: stETH/ETH
return 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
} else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {
// FIXED_PRICE: frxETH/ETH
return address(FIXED_PRICE);
return FIXED_PRICE;
} else if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {
// FIXED_PRICE: WETH/ETH
return FIXED_PRICE;
} else {
revert("Asset not available");
}
}
}

contract OETHOracleRouter is OracleRouter {
/**
* @notice Returns the total price in 8 digit USD for a given asset.
* This implementation does not (!) do range checks as the
* parent OracleRouter does.
* @param asset address of the asset
* @return uint256 USD price of 1 of the asset, in 8 decimal fixed
*/
function price(address asset)
external
view
virtual
override
returns (uint256)
{
address _feed = feed(asset);
if (_feed == FIXED_PRICE) {
return 1e8;
}
require(_feed != address(0), "Asset not available");
(, int256 _iprice, , , ) = AggregatorV3Interface(_feed)
.latestRoundData();
return uint256(_iprice);
}
}

contract OracleRouterDev is OracleRouterBase {
mapping(address => address) public assetToFeed;

Expand Down
38 changes: 0 additions & 38 deletions contracts/contracts/vault/VaultAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -448,44 +448,6 @@ contract VaultAdmin is VaultStorage {
IERC20(_asset).safeTransfer(governor(), _amount);
}

/***************************************
Pricing
****************************************/

/**
* @dev Returns the total price in 18 digit USD for a given asset.
* Never goes above 1, since that is how we price mints
* @param asset address of the asset
* @return uint256 USD price of 1 of the asset, in 18 decimal fixed
*/
function priceUSDMint(address asset) external view returns (uint256) {
uint256 price = IOracle(priceProvider).price(asset);
require(
price >= uint256(MINT_MINIMUM_UNIT_PRICE).scaleBy(8, 18),
"Asset price below peg"
);
if (price > 1e8) {
price = 1e8;
}
// Price from Oracle is returned with 8 decimals so scale to 18
return price.scaleBy(18, 8);
}

/**
* @dev Returns the total price in 18 digit USD for a given asset.
* Never goes below 1, since that is how we price redeems
* @param asset Address of the asset
* @return uint256 USD price of 1 of the asset, in 18 decimal fixed
*/
function priceUSDRedeem(address asset) external view returns (uint256) {
uint256 price = IOracle(priceProvider).price(asset);
if (price < 1e8) {
price = 1e8;
}
// Price from Oracle is returned with 8 decimals so scale to 18
return price.scaleBy(18, 8);
}

/***************************************
Strategies Admin
****************************************/
Expand Down
165 changes: 120 additions & 45 deletions contracts/contracts/vault/VaultCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

import { StableMath } from "../utils/StableMath.sol";
import { IOracle } from "../interfaces/IOracle.sol";
import { IVault } from "../interfaces/IVault.sol";
import { IOracle } from "../interfaces/IOracle.sol";
import { IBuyback } from "../interfaces/IBuyback.sol";
import { IBasicToken } from "../interfaces/IBasicToken.sol";
import { IGetExchangeRateToken } from "../interfaces/IGetExchangeRateToken.sol";
Expand Down Expand Up @@ -72,12 +72,7 @@ contract VaultCore is VaultStorage {
require(_amount > 0, "Amount must be greater than 0");

uint256 units = _toUnits(_amount, _asset);
uint256 price = IOracle(priceProvider).price(_asset) * 1e10;
uint256 unitPrice = _toUnitPrice(price, _asset);
if (unitPrice > 1e18) {
unitPrice = 1e18;
}
require(unitPrice >= MINT_MINIMUM_UNIT_PRICE, "Asset price below peg");
uint256 unitPrice = _toUnitPrice(_asset, true);
uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;

if (_minimumOusdAmount > 0) {
Expand Down Expand Up @@ -575,13 +570,7 @@ contract VaultCore is VaultStorage {
// Calculate totalOutputRatio
uint256 totalOutputRatio = 0;
for (uint256 i = 0; i < assetCount; i++) {
uint256 price = IOracle(priceProvider).price(allAssets[i]) * 1e10;
uint256 unitPrice = _toUnitPrice(price, allAssets[i]);
// Never give out more than one
// base token per unit of OUSD
if (unitPrice < 1e18) {
unitPrice = 1e18;
}
uint256 unitPrice = _toUnitPrice(allAssets[i], false);
uint256 ratio = assetUnits[i].mul(unitPrice).div(totalUnits);
totalOutputRatio = totalOutputRatio.add(ratio);
}
Expand All @@ -593,40 +582,56 @@ contract VaultCore is VaultStorage {
}

/***************************************
Utils
Pricing
****************************************/

/**
* @dev Return the number of assets supported by the Vault.
*/
function getAssetCount() public view returns (uint256) {
return allAssets.length;
}

/**
* @dev Return all asset addresses in order
* @dev Returns the total price in 18 digit units for a given asset.
* Never goes above 1, since that is how we price mints.
* @param asset address of the asset
* @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed
*/
function getAllAssets() external view returns (address[] memory) {
return allAssets;
}

/**
* @dev Return the number of strategies active on the Vault.
*/
function getStrategyCount() external view returns (uint256) {
return allStrategies.length;
function priceUnitMint(address asset)
external
view
returns (uint256 price)
{
/* need to supply 1 asset unit in asset's decimals and can not just hard-code
* to 1e18 and ignore calling `_toUnits` since we need to consider assets
* with the exchange rate
*/
uint256 units = _toUnits(
uint256(1e18).scaleBy(_getDecimals(asset), 18),
asset
);
price = _toUnitPrice(asset, true) * units;
}

/**
* @dev Return the array of all strategies
* @dev Returns the total price in 18 digit unit for a given asset.
* Never goes below 1, since that is how we price redeems
* @param asset Address of the asset
* @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed
*/
function getAllStrategies() external view returns (address[] memory) {
return allStrategies;
function priceUnitRedeem(address asset)
external
view
returns (uint256 price)
{
/* need to supply 1 asset unit in asset's decimals and can not just hard-code
* to 1e18 and ignore calling `_toUnits` since we need to consider assets
* with the exchange rate
*/
uint256 units = _toUnits(
uint256(1e18).scaleBy(_getDecimals(asset), 18),
asset
);
price = _toUnitPrice(asset, false) * units;
}

function isSupportedAsset(address _asset) external view returns (bool) {
return assets[_asset].isSupported;
}
/***************************************
Utils
****************************************/

/**
* @dev Convert a quantity of a token into 1e18 fixed decimal "units"
Expand Down Expand Up @@ -660,21 +665,59 @@ contract VaultCore is VaultStorage {
}
}

function _toUnitPrice(uint256 _price, address _asset)
/**
* @dev Returns asset's unit price accounting for different asset types
* and takes into account the context in which that price exists -
* - mint or redeem.
*
* Note: since we are returning the price of the unit and not the one of the
* asset (see comment above how 1 rETH exchanges for 1.2 units) we need
* to make the Oracle price adjustment as well since we are pricing the
* units and not the assets.
*
* The price also snaps to a "full unit price" in case a mint or redeem
* action would be unfavourable to the protocol.
*
*/
function _toUnitPrice(address _asset, bool isMint)
internal
view
returns (uint256)
returns (uint256 price)
{
UnitConversion conversion = assets[_asset].unitConversion;
if (conversion == UnitConversion.DECIMALS) {
return _price;
} else if (conversion == UnitConversion.GETEXCHANGERATE) {
price = IOracle(priceProvider).price(_asset) * 1e10;

if (conversion == UnitConversion.GETEXCHANGERATE) {
uint256 exchangeRate = IGetExchangeRateToken(_asset)
.getExchangeRate();
return (_price * 1e18) / exchangeRate;
} else {
price = (price * 1e18) / exchangeRate;
} else if (conversion != UnitConversion.DECIMALS) {
require(false, "Unsupported conversion type");
}

/* At this stage the price is already adjusted to the unit
* so the price checks are agnostic to underlying asset being
* pegged to a USD or to an ETH or having a custom exchange rate.
*/
require(price <= MAX_UNIT_PRICE_DRIFT, "Vault: Price exceeds max");
require(price >= MIN_UNIT_PRICE_DRIFT, "Vault: Price under min");

if (isMint) {
/* Never price a normalized unit price for more than one
* unit of OETH/OUSD when minting.
*/
if (price > 1e18) {
price = 1e18;
}
require(price >= MINT_MINIMUM_ORACLE, "Asset price below peg");
} else {
/* Never give out more than 1 normalized unit amount of assets
* for one unit of OETH/OUSD when redeeming.
*/
if (price < 1e18) {
price = 1e18;
}
}
}

function _getDecimals(address _asset) internal view returns (uint256) {
Expand All @@ -683,6 +726,38 @@ contract VaultCore is VaultStorage {
return decimals;
}

/**
* @dev Return the number of assets supported by the Vault.
*/
function getAssetCount() public view returns (uint256) {
return allAssets.length;
}

/**
* @dev Return all asset addresses in order
*/
function getAllAssets() external view returns (address[] memory) {
return allAssets;
}

/**
* @dev Return the number of strategies active on the Vault.
*/
function getStrategyCount() external view returns (uint256) {
return allStrategies.length;
}

/**
* @dev Return the array of all strategies
*/
function getAllStrategies() external view returns (address[] memory) {
return allStrategies;
}

function isSupportedAsset(address _asset) external view returns (bool) {
return assets[_asset].isSupported;
}

/**
* @dev Falldown to the admin implementation
* @notice This is a catch all for all functions not declared in core
Expand Down
Loading