Last active
May 15, 2023 13:26
-
-
Save DanielVF/7c4b270d7dafd31b1b8662f4a19cd649 to your computer and use it in GitHub Desktop.
VaultCore Upgrade
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
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
/** | |
* @title OUSD Vault Contract | |
* @notice The Vault contract stores assets. On a deposit, OUSD will be minted | |
and sent to the depositor. On a withdrawal, OUSD will be burned and | |
assets will be sent to the withdrawer. The Vault accepts deposits of | |
interest from yield bearing strategies which will modify the supply | |
of OUSD. | |
* @author Origin Protocol Inc | |
*/ | |
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol"; | |
import "@openzeppelin/contracts/utils/Strings.sol"; | |
import { StableMath } from "../utils/StableMath.sol"; | |
import { IVault } from "../interfaces/IVault.sol"; | |
import { IOracle } from "../interfaces/IOracle.sol"; | |
import { IBasicToken } from "../interfaces/IBasicToken.sol"; | |
import { IGetExchangeRateToken } from "../interfaces/IGetExchangeRateToken.sol"; | |
import "./VaultStorage.sol"; | |
contract VaultCore is VaultStorage { | |
using SafeERC20 for IERC20; | |
using StableMath for uint256; | |
using SafeMath for uint256; | |
// max signed int | |
uint256 constant MAX_INT = 2**255 - 1; | |
// max un-signed int | |
uint256 constant MAX_UINT = | |
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; | |
/** | |
* @dev Verifies that the rebasing is not paused. | |
*/ | |
modifier whenNotRebasePaused() { | |
require(!rebasePaused, "Rebasing paused"); | |
_; | |
} | |
/** | |
* @dev Verifies that the deposits are not paused. | |
*/ | |
modifier whenNotCapitalPaused() { | |
require(!capitalPaused, "Capital paused"); | |
_; | |
} | |
modifier onlyOusdMetaStrategy() { | |
require( | |
msg.sender == ousdMetaStrategy, | |
"Caller is not the OUSD meta strategy" | |
); | |
_; | |
} | |
/** | |
* @dev Deposit a supported asset and mint OUSD. | |
* @param _asset Address of the asset being deposited | |
* @param _amount Amount of the asset being deposited | |
* @param _minimumOusdAmount Minimum OUSD to mint | |
*/ | |
function mint( | |
address _asset, | |
uint256 _amount, | |
uint256 _minimumOusdAmount | |
) external whenNotCapitalPaused nonReentrant { | |
require(assets[_asset].isSupported, "Asset is not supported"); | |
require(_amount > 0, "Amount must be greater than 0"); | |
uint256 units = _toUnits(_amount, _asset); | |
uint256 unitPrice = _toUnitPrice(_asset, true); | |
uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18; | |
if (_minimumOusdAmount > 0) { | |
require( | |
priceAdjustedDeposit >= _minimumOusdAmount, | |
"Mint amount lower than minimum" | |
); | |
} | |
emit Mint(msg.sender, priceAdjustedDeposit); | |
// Rebase must happen before any transfers occur. | |
if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) { | |
_rebase(); | |
} | |
// Mint matching OUSD | |
oUSD.mint(msg.sender, priceAdjustedDeposit); | |
// Transfer the deposited coins to the vault | |
IERC20 asset = IERC20(_asset); | |
asset.safeTransferFrom(msg.sender, address(this), _amount); | |
if (priceAdjustedDeposit >= autoAllocateThreshold) { | |
_allocate(); | |
} | |
} | |
/** | |
* @dev Mint OUSD for OUSD Meta Strategy | |
* @param _amount Amount of the asset being deposited | |
* | |
* Notice: can't use `nonReentrant` modifier since the `mint` function can | |
* call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function | |
* while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision. | |
* | |
* Also important to understand is that this is a limitation imposed by the test suite. | |
* Production / mainnet contracts should never be configured in a way where mint/redeem functions | |
* that are moving funds between the Vault and end user wallets can influence strategies | |
* utilizing this function. | |
*/ | |
function mintForStrategy(uint256 _amount) | |
external | |
whenNotCapitalPaused | |
onlyOusdMetaStrategy | |
{ | |
require(_amount < MAX_INT, "Amount too high"); | |
emit Mint(msg.sender, _amount); | |
// Rebase must happen before any transfers occur. | |
// TODO: double check the relevance of this | |
if (_amount >= rebaseThreshold && !rebasePaused) { | |
_rebase(); | |
} | |
// safe to cast because of the require check at the beginning of the function | |
netOusdMintedForStrategy += int256(_amount); | |
require( | |
abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold, | |
"Minted ousd surpassed netOusdMintForStrategyThreshold." | |
); | |
// Mint matching OUSD | |
oUSD.mint(msg.sender, _amount); | |
} | |
// In memoriam | |
/** | |
* @dev Withdraw a supported asset and burn OUSD. | |
* @param _amount Amount of OUSD to burn | |
* @param _minimumUnitAmount Minimum stablecoin units to receive in return | |
*/ | |
function redeem(uint256 _amount, uint256 _minimumUnitAmount) | |
external | |
whenNotCapitalPaused | |
nonReentrant | |
{ | |
_redeem(_amount, _minimumUnitAmount); | |
} | |
/** | |
* @dev Withdraw a supported asset and burn OUSD. | |
* @param _amount Amount of OUSD to burn | |
* @param _minimumUnitAmount Minimum stablecoin units to receive in return | |
*/ | |
function _redeem(uint256 _amount, uint256 _minimumUnitAmount) internal { | |
// Calculate redemption outputs | |
( | |
uint256[] memory outputs, | |
uint256 _backingValue | |
) = _calculateRedeemOutputs(_amount); | |
// Check that OUSD is backed by enough assets | |
uint256 _totalSupply = oUSD.totalSupply(); | |
if (maxSupplyDiff > 0) { | |
// Allow a max difference of maxSupplyDiff% between | |
// backing assets value and OUSD total supply | |
uint256 diff = _totalSupply.divPrecisely(_backingValue); | |
require( | |
(diff > 1e18 ? diff.sub(1e18) : uint256(1e18).sub(diff)) <= | |
maxSupplyDiff, | |
"Backing supply liquidity error" | |
); | |
} | |
emit Redeem(msg.sender, _amount); | |
// Send outputs | |
for (uint256 i = 0; i < allAssets.length; i++) { | |
if (outputs[i] == 0) continue; | |
IERC20 asset = IERC20(allAssets[i]); | |
if (asset.balanceOf(address(this)) >= outputs[i]) { | |
// Use Vault funds first if sufficient | |
asset.safeTransfer(msg.sender, outputs[i]); | |
} else { | |
address strategyAddr = assetDefaultStrategies[allAssets[i]]; | |
if (strategyAddr != address(0)) { | |
// Nothing in Vault, but something in Strategy, send from there | |
IStrategy strategy = IStrategy(strategyAddr); | |
strategy.withdraw(msg.sender, allAssets[i], outputs[i]); | |
} else { | |
// Cant find funds anywhere | |
revert("Liquidity error"); | |
} | |
} | |
} | |
if (_minimumUnitAmount > 0) { | |
uint256 unitTotal = 0; | |
for (uint256 i = 0; i < outputs.length; i++) { | |
unitTotal += _toUnits(outputs[i], allAssets[i]); | |
} | |
require( | |
unitTotal >= _minimumUnitAmount, | |
"Redeem amount lower than minimum" | |
); | |
} | |
oUSD.burn(msg.sender, _amount); | |
// Until we can prove that we won't affect the prices of our assets | |
// by withdrawing them, this should be here. | |
// It's possible that a strategy was off on its asset total, perhaps | |
// a reward token sold for more or for less than anticipated. | |
if (_amount >= rebaseThreshold && !rebasePaused) { | |
_rebase(); | |
} | |
} | |
/** | |
* @dev Burn OUSD for OUSD Meta Strategy | |
* @param _amount Amount of OUSD to burn | |
* | |
* Notice: can't use `nonReentrant` modifier since the `redeem` function could | |
* require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy` | |
* while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision. | |
* | |
* Also important to understand is that this is a limitation imposed by the test suite. | |
* Production / mainnet contracts should never be configured in a way where mint/redeem functions | |
* that are moving funds between the Vault and end user wallets can influence strategies | |
* utilizing this function. | |
*/ | |
function burnForStrategy(uint256 _amount) | |
external | |
whenNotCapitalPaused | |
onlyOusdMetaStrategy | |
{ | |
require(_amount < MAX_INT, "Amount too high"); | |
emit Redeem(msg.sender, _amount); | |
// safe to cast because of the require check at the beginning of the function | |
netOusdMintedForStrategy -= int256(_amount); | |
require( | |
abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold, | |
"Attempting to burn too much OUSD." | |
); | |
// Burn OUSD | |
oUSD.burn(msg.sender, _amount); | |
// Until we can prove that we won't affect the prices of our assets | |
// by withdrawing them, this should be here. | |
// It's possible that a strategy was off on its asset total, perhaps | |
// a reward token sold for more or for less than anticipated. | |
if (_amount >= rebaseThreshold && !rebasePaused) { | |
_rebase(); | |
} | |
} | |
/** | |
* @notice Withdraw a supported asset and burn all OUSD. | |
* @param _minimumUnitAmount Minimum stablecoin units to receive in return | |
*/ | |
function redeemAll(uint256 _minimumUnitAmount) | |
external | |
whenNotCapitalPaused | |
nonReentrant | |
{ | |
_redeem(oUSD.balanceOf(msg.sender), _minimumUnitAmount); | |
} | |
/** | |
* @notice Allocate unallocated funds on Vault to strategies. | |
* @dev Allocate unallocated funds on Vault to strategies. | |
**/ | |
function allocate() external whenNotCapitalPaused nonReentrant { | |
_allocate(); | |
} | |
/** | |
* @notice Allocate unallocated funds on Vault to strategies. | |
* @dev Allocate unallocated funds on Vault to strategies. | |
**/ | |
function _allocate() internal { | |
uint256 vaultValue = _totalValueInVault(); | |
// Nothing in vault to allocate | |
if (vaultValue == 0) return; | |
uint256 strategiesValue = _totalValueInStrategies(); | |
// We have a method that does the same as this, gas optimisation | |
uint256 calculatedTotalValue = vaultValue.add(strategiesValue); | |
// We want to maintain a buffer on the Vault so calculate a percentage | |
// modifier to multiply each amount being allocated by to enforce the | |
// vault buffer | |
uint256 vaultBufferModifier; | |
if (strategiesValue == 0) { | |
// Nothing in Strategies, allocate 100% minus the vault buffer to | |
// strategies | |
vaultBufferModifier = uint256(1e18).sub(vaultBuffer); | |
} else { | |
vaultBufferModifier = vaultBuffer.mul(calculatedTotalValue).div( | |
vaultValue | |
); | |
if (1e18 > vaultBufferModifier) { | |
// E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17 | |
// (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault | |
vaultBufferModifier = uint256(1e18).sub(vaultBufferModifier); | |
} else { | |
// We need to let the buffer fill | |
return; | |
} | |
} | |
if (vaultBufferModifier == 0) return; | |
// Iterate over all assets in the Vault and allocate to the appropriate | |
// strategy | |
for (uint256 i = 0; i < allAssets.length; i++) { | |
IERC20 asset = IERC20(allAssets[i]); | |
uint256 assetBalance = asset.balanceOf(address(this)); | |
// No balance, nothing to do here | |
if (assetBalance == 0) continue; | |
// Multiply the balance by the vault buffer modifier and truncate | |
// to the scale of the asset decimals | |
uint256 allocateAmount = assetBalance.mulTruncate( | |
vaultBufferModifier | |
); | |
address depositStrategyAddr = assetDefaultStrategies[ | |
address(asset) | |
]; | |
if (depositStrategyAddr != address(0) && allocateAmount > 0) { | |
IStrategy strategy = IStrategy(depositStrategyAddr); | |
// Transfer asset to Strategy and call deposit method to | |
// mint or take required action | |
asset.safeTransfer(address(strategy), allocateAmount); | |
strategy.deposit(address(asset), allocateAmount); | |
emit AssetAllocated( | |
address(asset), | |
depositStrategyAddr, | |
allocateAmount | |
); | |
} | |
} | |
} | |
/** | |
* @dev Calculate the total value of assets held by the Vault and all | |
* strategies and update the supply of OUSD. | |
*/ | |
function rebase() external virtual nonReentrant { | |
_rebase(); | |
} | |
/** | |
* @dev Calculate the total value of assets held by the Vault and all | |
* strategies and update the supply of OUSD, optionally sending a | |
* portion of the yield to the trustee. | |
*/ | |
function _rebase() internal whenNotRebasePaused { | |
uint256 ousdSupply = oUSD.totalSupply(); | |
if (ousdSupply == 0) { | |
return; | |
} | |
uint256 vaultValue = _totalValue(); | |
// Yield fee collection | |
address _trusteeAddress = trusteeAddress; // gas savings | |
if (_trusteeAddress != address(0) && (vaultValue > ousdSupply)) { | |
uint256 yield = vaultValue.sub(ousdSupply); | |
uint256 fee = yield.mul(trusteeFeeBps).div(10000); | |
require(yield > fee, "Fee must not be greater than yield"); | |
if (fee > 0) { | |
oUSD.mint(_trusteeAddress, fee); | |
} | |
emit YieldDistribution(_trusteeAddress, yield, fee); | |
} | |
// Only rachet OUSD supply upwards | |
ousdSupply = oUSD.totalSupply(); // Final check should use latest value | |
if (vaultValue > ousdSupply) { | |
oUSD.changeSupply(vaultValue); | |
} | |
} | |
/** | |
* @dev Determine the total value of assets held by the vault and its | |
* strategies. | |
* @return value Total value in USD (1e18) | |
*/ | |
function totalValue() external view virtual returns (uint256 value) { | |
value = _totalValue(); | |
} | |
/** | |
* @dev Internal Calculate the total value of the assets held by the | |
* vault and its strategies. | |
* @return value Total value in USD (1e18) | |
*/ | |
function _totalValue() internal view virtual returns (uint256 value) { | |
return _totalValueInVault().add(_totalValueInStrategies()); | |
} | |
/** | |
* @dev Internal to calculate total value of all assets held in Vault. | |
* @return value Total value in ETH (1e18) | |
*/ | |
function _totalValueInVault() internal view returns (uint256 value) { | |
for (uint256 y = 0; y < allAssets.length; y++) { | |
IERC20 asset = IERC20(allAssets[y]); | |
uint256 balance = asset.balanceOf(address(this)); | |
if (balance > 0) { | |
value += _toUnits(balance, allAssets[y]); | |
} | |
} | |
} | |
/** | |
* @dev Internal to calculate total value of all assets held in Strategies. | |
* @return value Total value in ETH (1e18) | |
*/ | |
function _totalValueInStrategies() internal view returns (uint256 value) { | |
for (uint256 i = 0; i < allStrategies.length; i++) { | |
value = value.add(_totalValueInStrategy(allStrategies[i])); | |
} | |
} | |
/** | |
* @dev Internal to calculate total value of all assets held by strategy. | |
* @param _strategyAddr Address of the strategy | |
* @return value Total value in ETH (1e18) | |
*/ | |
function _totalValueInStrategy(address _strategyAddr) | |
internal | |
view | |
returns (uint256 value) | |
{ | |
IStrategy strategy = IStrategy(_strategyAddr); | |
for (uint256 y = 0; y < allAssets.length; y++) { | |
if (strategy.supportsAsset(allAssets[y])) { | |
uint256 balance = strategy.checkBalance(allAssets[y]); | |
if (balance > 0) { | |
value += _toUnits(balance, allAssets[y]); | |
} | |
} | |
} | |
} | |
/** | |
* @notice Get the balance of an asset held in Vault and all strategies. | |
* @param _asset Address of asset | |
* @return uint256 Balance of asset in decimals of asset | |
*/ | |
function checkBalance(address _asset) external view returns (uint256) { | |
return _checkBalance(_asset); | |
} | |
/** | |
* @notice Get the balance of an asset held in Vault and all strategies. | |
* @param _asset Address of asset | |
* @return balance Balance of asset in decimals of asset | |
*/ | |
function _checkBalance(address _asset) | |
internal | |
view | |
virtual | |
returns (uint256 balance) | |
{ | |
IERC20 asset = IERC20(_asset); | |
balance = asset.balanceOf(address(this)); | |
for (uint256 i = 0; i < allStrategies.length; i++) { | |
IStrategy strategy = IStrategy(allStrategies[i]); | |
if (strategy.supportsAsset(_asset)) { | |
balance = balance.add(strategy.checkBalance(_asset)); | |
} | |
} | |
} | |
/** | |
* @notice Calculate the outputs for a redeem function, i.e. the mix of | |
* coins that will be returned | |
*/ | |
function calculateRedeemOutputs(uint256 _amount) | |
external | |
view | |
returns (uint256[] memory) | |
{ | |
(uint256[] memory outputs, ) = _calculateRedeemOutputs(_amount); | |
return outputs; | |
} | |
/** | |
* @notice Calculate the outputs for a redeem function, i.e. the mix of | |
* coins that will be returned. | |
* @return outputs Array of amounts respective to the supported assets | |
* @return totalUnits Total balance of Vault in units | |
*/ | |
function _calculateRedeemOutputs(uint256 _amount) | |
internal | |
view | |
returns (uint256[] memory outputs, uint256 totalUnits) | |
{ | |
// We always give out coins in proportion to how many we have, | |
// Now if all coins were the same value, this math would easy, | |
// just take the percentage of each coin, and multiply by the | |
// value to be given out. But if coins are worth more than $1, | |
// then we would end up handing out too many coins. We need to | |
// adjust by the total value of coins. | |
// | |
// To do this, we total up the value of our coins, by their | |
// percentages. Then divide what we would otherwise give out by | |
// this number. | |
// | |
// Let say we have 100 DAI at $1.06 and 200 USDT at $1.00. | |
// So for every 1 DAI we give out, we'll be handing out 2 USDT | |
// Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02 | |
// | |
// So when calculating the output, we take the percentage of | |
// each coin, times the desired output value, divided by the | |
// totalOutputRatio. | |
// | |
// For example, withdrawing: 30 OUSD: | |
// DAI 33% * 30 / 1.02 = 9.80 DAI | |
// USDT = 66 % * 30 / 1.02 = 19.60 USDT | |
// | |
// Checking these numbers: | |
// 9.80 DAI * 1.06 = $10.40 | |
// 19.60 USDT * 1.00 = $19.60 | |
// | |
// And so the user gets $10.40 + $19.60 = $30 worth of value. | |
uint256 assetCount = allAssets.length; | |
uint256[] memory assetUnits = new uint256[](assetCount); | |
uint256[] memory assetBalances = new uint256[](assetCount); | |
outputs = new uint256[](assetCount); | |
// Calculate redeem fee | |
if (redeemFeeBps > 0) { | |
uint256 redeemFee = _amount.mul(redeemFeeBps).div(10000); | |
_amount = _amount.sub(redeemFee); | |
} | |
// Calculate assets balances and decimals once, | |
// for a large gas savings. | |
for (uint256 i = 0; i < assetCount; i++) { | |
uint256 balance = _checkBalance(allAssets[i]); | |
assetBalances[i] = balance; | |
assetUnits[i] = _toUnits(balance, allAssets[i]); | |
totalUnits = totalUnits.add(assetUnits[i]); | |
} | |
// Calculate totalOutputRatio | |
uint256 totalOutputRatio = 0; | |
for (uint256 i = 0; i < assetCount; i++) { | |
uint256 unitPrice = _toUnitPrice(allAssets[i], false); | |
uint256 ratio = assetUnits[i].mul(unitPrice).div(totalUnits); | |
totalOutputRatio = totalOutputRatio.add(ratio); | |
} | |
// Calculate final outputs | |
uint256 factor = _amount.divPrecisely(totalOutputRatio); | |
for (uint256 i = 0; i < assetCount; i++) { | |
outputs[i] = assetBalances[i].mul(factor).div(totalUnits); | |
} | |
} | |
/*************************************** | |
Pricing | |
****************************************/ | |
/** | |
* @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 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) / 1e18; | |
} | |
/** | |
* @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 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) / 1e18; | |
} | |
/*************************************** | |
Utils | |
****************************************/ | |
/** | |
* @dev Convert a quantity of a token into 1e18 fixed decimal "units" | |
* in the underlying base (USD/ETH) used by the vault. | |
* Price is not taken into account, only quantity. | |
* | |
* Examples of this conversion: | |
* | |
* - 1e18 DAI becomes 1e18 units (same decimals) | |
* - 1e6 USDC becomes 1e18 units (decimal conversion) | |
* - 1e18 rETH becomes 1.2e18 units (exchange rate conversion) | |
* | |
* @param _raw Quantity of asset | |
* @param _asset Core Asset address | |
* @return value 1e18 normalized quantity of units | |
*/ | |
function _toUnits(uint256 _raw, address _asset) | |
internal | |
view | |
returns (uint256) | |
{ | |
UnitConversion conversion = assets[_asset].unitConversion; | |
if (conversion == UnitConversion.DECIMALS) { | |
return _raw.scaleBy(18, _getDecimals(_asset)); | |
} else if (conversion == UnitConversion.GETEXCHANGERATE) { | |
uint256 exchangeRate = IGetExchangeRateToken(_asset) | |
.getExchangeRate(); | |
return (_raw * exchangeRate) / 1e18; | |
} else { | |
require(false, "Unsupported conversion type"); | |
} | |
} | |
/** | |
* @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 price) | |
{ | |
UnitConversion conversion = assets[_asset].unitConversion; | |
price = IOracle(priceProvider).price(_asset); | |
if (conversion == UnitConversion.GETEXCHANGERATE) { | |
uint256 exchangeRate = IGetExchangeRateToken(_asset) | |
.getExchangeRate(); | |
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_UNIT_PRICE, "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) { | |
uint256 decimals = assets[_asset].decimals; | |
require(decimals > 0, "Decimals not cached"); | |
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 | |
*/ | |
// solhint-disable-next-line no-complex-fallback | |
fallback() external payable { | |
bytes32 slot = adminImplPosition; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { | |
// Copy msg.data. We take full control of memory in this inline assembly | |
// block because it will not return to Solidity code. We overwrite the | |
// Solidity scratch pad at memory position 0. | |
calldatacopy(0, 0, calldatasize()) | |
// Call the implementation. | |
// out and outsize are 0 because we don't know the size yet. | |
let result := delegatecall( | |
gas(), | |
sload(slot), | |
0, | |
calldatasize(), | |
0, | |
0 | |
) | |
// Copy the returned data. | |
returndatacopy(0, 0, returndatasize()) | |
switch result | |
// delegatecall returns 0 on error. | |
case 0 { | |
revert(0, returndatasize()) | |
} | |
default { | |
return(0, returndatasize()) | |
} | |
} | |
} | |
function abs(int256 x) private pure returns (uint256) { | |
require(x < int256(MAX_INT), "Amount too high"); | |
return x >= 0 ? uint256(x) : uint256(-x); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment