Contract Name:
ValueInterpreter
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "./IDerivativePriceFeed.sol";
/// @title IDerivativePriceFeed Interface
/// @author Enzyme Council <security@enzyme.finance>
interface IAggregatedDerivativePriceFeed is IDerivativePriceFeed {
function getPriceFeedForDerivative(address) external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IDerivativePriceFeed Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Simple interface for derivative price source oracle implementations
interface IDerivativePriceFeed {
function calcUnderlyingValues(address, uint256)
external
returns (address[] memory, uint256[] memory);
function isSupportedAsset(address) external view returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IPrimitivePriceFeed Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for primitive price feeds
interface IPrimitivePriceFeed {
function calcCanonicalValue(
address,
uint256,
address
) external view returns (uint256, bool);
function calcLiveValue(
address,
uint256,
address
) external view returns (uint256, bool);
function isSupportedAsset(address) external view returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IValueInterpreter interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for ValueInterpreter
interface IValueInterpreter {
function calcCanonicalAssetValue(
address,
uint256,
address
) external returns (uint256, bool);
function calcCanonicalAssetsTotalValue(
address[] calldata,
uint256[] calldata,
address
) external returns (uint256, bool);
function calcLiveAssetValue(
address,
uint256,
address
) external returns (uint256, bool);
function calcLiveAssetsTotalValue(
address[] calldata,
uint256[] calldata,
address
) external returns (uint256, bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../price-feeds/derivatives/IAggregatedDerivativePriceFeed.sol";
import "../price-feeds/derivatives/IDerivativePriceFeed.sol";
import "../price-feeds/primitives/IPrimitivePriceFeed.sol";
import "./IValueInterpreter.sol";
/// @title ValueInterpreter Contract
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interprets price feeds to provide covert value between asset pairs
/// @dev This contract contains several "live" value calculations, which for this release are simply
/// aliases to their "canonical" value counterparts since the only primitive price feed (Chainlink)
/// is immutable in this contract and only has one type of value. Including the "live" versions of
/// functions only serves as a placeholder for infrastructural components and plugins (e.g., policies)
/// to explicitly define the types of values that they should (and will) be using in a future release.
contract ValueInterpreter is IValueInterpreter {
using SafeMath for uint256;
address private immutable AGGREGATED_DERIVATIVE_PRICE_FEED;
address private immutable PRIMITIVE_PRICE_FEED;
constructor(address _primitivePriceFeed, address _aggregatedDerivativePriceFeed) public {
AGGREGATED_DERIVATIVE_PRICE_FEED = _aggregatedDerivativePriceFeed;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
}
// EXTERNAL FUNCTIONS
/// @notice An alias of calcCanonicalAssetsTotalValue
function calcLiveAssetsTotalValue(
address[] calldata _baseAssets,
uint256[] calldata _amounts,
address _quoteAsset
) external override returns (uint256 value_, bool isValid_) {
return calcCanonicalAssetsTotalValue(_baseAssets, _amounts, _quoteAsset);
}
/// @notice An alias of calcCanonicalAssetValue
function calcLiveAssetValue(
address _baseAsset,
uint256 _amount,
address _quoteAsset
) external override returns (uint256 value_, bool isValid_) {
return calcCanonicalAssetValue(_baseAsset, _amount, _quoteAsset);
}
// PUBLIC FUNCTIONS
/// @notice Calculates the total value of given amounts of assets in a single quote asset
/// @param _baseAssets The assets to convert
/// @param _amounts The amounts of the _baseAssets to convert
/// @param _quoteAsset The asset to which to convert
/// @return value_ The sum value of _baseAssets, denominated in the _quoteAsset
/// @return isValid_ True if the price feed rates used to derive value are all valid
/// @dev Does not alter protocol state,
/// but not a view because calls to price feeds can potentially update third party state
function calcCanonicalAssetsTotalValue(
address[] memory _baseAssets,
uint256[] memory _amounts,
address _quoteAsset
) public override returns (uint256 value_, bool isValid_) {
require(
_baseAssets.length == _amounts.length,
"calcCanonicalAssetsTotalValue: Arrays unequal lengths"
);
require(
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_quoteAsset),
"calcCanonicalAssetsTotalValue: Unsupported _quoteAsset"
);
isValid_ = true;
for (uint256 i; i < _baseAssets.length; i++) {
(uint256 assetValue, bool assetValueIsValid) = __calcAssetValue(
_baseAssets[i],
_amounts[i],
_quoteAsset
);
value_ = value_.add(assetValue);
if (!assetValueIsValid) {
isValid_ = false;
}
}
return (value_, isValid_);
}
/// @notice Calculates the value of a given amount of one asset in terms of another asset
/// @param _baseAsset The asset from which to convert
/// @param _amount The amount of the _baseAsset to convert
/// @param _quoteAsset The asset to which to convert
/// @return value_ The equivalent quantity in the _quoteAsset
/// @return isValid_ True if the price feed rates used to derive value are all valid
/// @dev Does not alter protocol state,
/// but not a view because calls to price feeds can potentially update third party state
function calcCanonicalAssetValue(
address _baseAsset,
uint256 _amount,
address _quoteAsset
) public override returns (uint256 value_, bool isValid_) {
if (_baseAsset == _quoteAsset || _amount == 0) {
return (_amount, true);
}
require(
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_quoteAsset),
"calcCanonicalAssetValue: Unsupported _quoteAsset"
);
return __calcAssetValue(_baseAsset, _amount, _quoteAsset);
}
// PRIVATE FUNCTIONS
/// @dev Helper to differentially calculate an asset value
/// based on if it is a primitive or derivative asset.
function __calcAssetValue(
address _baseAsset,
uint256 _amount,
address _quoteAsset
) private returns (uint256 value_, bool isValid_) {
if (_baseAsset == _quoteAsset || _amount == 0) {
return (_amount, true);
}
// Handle case that asset is a primitive
if (IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_baseAsset)) {
return
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).calcCanonicalValue(
_baseAsset,
_amount,
_quoteAsset
);
}
// Handle case that asset is a derivative
address derivativePriceFeed = IAggregatedDerivativePriceFeed(
AGGREGATED_DERIVATIVE_PRICE_FEED
)
.getPriceFeedForDerivative(_baseAsset);
if (derivativePriceFeed != address(0)) {
return __calcDerivativeValue(derivativePriceFeed, _baseAsset, _amount, _quoteAsset);
}
revert("__calcAssetValue: Unsupported _baseAsset");
}
/// @dev Helper to calculate the value of a derivative in an arbitrary asset.
/// Handles multiple underlying assets (e.g., Uniswap and Balancer pool tokens).
/// Handles underlying assets that are also derivatives (e.g., a cDAI-ETH LP)
function __calcDerivativeValue(
address _derivativePriceFeed,
address _derivative,
uint256 _amount,
address _quoteAsset
) private returns (uint256 value_, bool isValid_) {
(address[] memory underlyings, uint256[] memory underlyingAmounts) = IDerivativePriceFeed(
_derivativePriceFeed
)
.calcUnderlyingValues(_derivative, _amount);
require(underlyings.length > 0, "__calcDerivativeValue: No underlyings");
require(
underlyings.length == underlyingAmounts.length,
"__calcDerivativeValue: Arrays unequal lengths"
);
// Let validity be negated if any of the underlying value calculations are invalid
isValid_ = true;
for (uint256 i = 0; i < underlyings.length; i++) {
(uint256 underlyingValue, bool underlyingValueIsValid) = __calcAssetValue(
underlyings[i],
underlyingAmounts[i],
_quoteAsset
);
if (!underlyingValueIsValid) {
isValid_ = false;
}
value_ = value_.add(underlyingValue);
}
}
///////////////////
// STATE GETTERS //
///////////////////
/// @notice Gets the `AGGREGATED_DERIVATIVE_PRICE_FEED` variable
/// @return aggregatedDerivativePriceFeed_ The `AGGREGATED_DERIVATIVE_PRICE_FEED` variable value
function getAggregatedDerivativePriceFeed()
external
view
returns (address aggregatedDerivativePriceFeed_)
{
return AGGREGATED_DERIVATIVE_PRICE_FEED;
}
/// @notice Gets the `PRIMITIVE_PRICE_FEED` variable
/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable value
function getPrimitivePriceFeed() external view returns (address primitivePriceFeed_) {
return PRIMITIVE_PRICE_FEED;
}
}