// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../IIntegrationAdapter.sol";
import"./IntegrationSelectors.sol";
/// @title AdapterBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A base contract for integration adaptersabstractcontractAdapterBaseisIIntegrationAdapter, IntegrationSelectors{
usingSafeERC20forERC20;
addressinternalimmutable INTEGRATION_MANAGER;
/// @dev Provides a standard implementation for transferring assets between/// the fund's VaultProxy and the adapter, by wrapping the adapter action./// This modifier should be implemented in almost all adapter actions, unless they/// do not move assets or can spend and receive assets directly with the VaultProxymodifierfundAssetsTransferHandler(address _vaultProxy,
bytesmemory _encodedAssetTransferArgs
) {
(
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType,
address[] memory spendAssets,
uint256[] memory spendAssetAmounts,
address[] memory incomingAssets
) = __decodeEncodedAssetTransferArgs(_encodedAssetTransferArgs);
// Take custody of spend assets (if necessary)if (spendAssetsHandleType == IIntegrationManager.SpendAssetsHandleType.Approve) {
for (uint256 i =0; i < spendAssets.length; i++) {
ERC20(spendAssets[i]).safeTransferFrom(
_vaultProxy,
address(this),
spendAssetAmounts[i]
);
}
}
// Execute call_;
// Transfer remaining assets back to the fund's VaultProxy
__transferContractAssetBalancesToFund(_vaultProxy, incomingAssets);
__transferContractAssetBalancesToFund(_vaultProxy, spendAssets);
}
modifieronlyIntegrationManager{
require(
msg.sender== INTEGRATION_MANAGER,
"Only the IntegrationManager can call this function"
);
_;
}
constructor(address _integrationManager) public{
INTEGRATION_MANAGER = _integrationManager;
}
// INTERNAL FUNCTIONS/// @dev Helper for adapters to approve their integratees with the max amount of an asset./// Since everything is done atomically, and only the balances to-be-used are sent to adapters,/// there is no need to approve exact amounts on every call.function__approveMaxAsNeeded(address _asset,
address _target,
uint256 _neededAmount
) internal{
if (ERC20(_asset).allowance(address(this), _target) < _neededAmount) {
ERC20(_asset).approve(_target, type(uint256).max);
}
}
/// @dev Helper to decode the _encodedAssetTransferArgs param passed to adapter callfunction__decodeEncodedAssetTransferArgs(bytesmemory _encodedAssetTransferArgs)
internalpurereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_
)
{
returnabi.decode(
_encodedAssetTransferArgs,
(IIntegrationManager.SpendAssetsHandleType, address[], uint256[], address[])
);
}
/// @dev Helper to transfer full contract balances of assets to the specified VaultProxyfunction__transferContractAssetBalancesToFund(address _vaultProxy, address[] memory _assets)
private{
for (uint256 i =0; i < _assets.length; i++) {
uint256 postCallAmount = ERC20(_assets[i]).balanceOf(address(this));
if (postCallAmount >0) {
ERC20(_assets[i]).safeTransfer(_vaultProxy, postCallAmount);
}
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `INTEGRATION_MANAGER` variable/// @return integrationManager_ The `INTEGRATION_MANAGER` variable valuefunctiongetIntegrationManager() externalviewreturns (address integrationManager_) {
return INTEGRATION_MANAGER;
}
}
Contract Source Code
File 2 of 138: AdapterBlacklist.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/AddressListPolicyMixin.sol";
import"./utils/PreCallOnIntegrationValidatePolicyBase.sol";
/// @title AdapterBlacklist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that disallows a configurable blacklist of adapters from use by a fundcontractAdapterBlacklistisPreCallOnIntegrationValidatePolicyBase, AddressListPolicyMixin{
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
__addToList(_comptrollerProxy, abi.decode(_encodedSettings, (address[])));
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ADAPTER_BLACKLIST";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _adapter The adapter with which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address _adapter)
publicviewreturns (bool isValid_)
{
return!isInList(_comptrollerProxy, _adapter);
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(address adapter, ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, adapter);
}
}
Contract Source Code
File 3 of 138: AdapterWhitelist.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/AddressListPolicyMixin.sol";
import"./utils/PreCallOnIntegrationValidatePolicyBase.sol";
/// @title AdapterWhitelist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that only allows a configurable whitelist of adapters for use by a fundcontractAdapterWhitelistisPreCallOnIntegrationValidatePolicyBase, AddressListPolicyMixin{
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
__addToList(_comptrollerProxy, abi.decode(_encodedSettings, (address[])));
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ADAPTER_WHITELIST";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _adapter The adapter with which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address _adapter)
publicviewreturns (bool isValid_)
{
return isInList(_comptrollerProxy, _adapter);
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(address adapter, ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, adapter);
}
}
Contract Source Code
File 4 of 138: Address.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.2;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in// construction, since the code is only stored at the end of the// constructor execution.uint256 size;
// solhint-disable-next-line no-inline-assemblyassembly { size :=extcodesize(account) }
return size >0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data, stringmemory errorMessage) internalreturns (bytesmemory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value, stringmemory errorMessage) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function_functionCallWithValue(address target, bytesmemory data, uint256 weiValue, stringmemory errorMessage) privatereturns (bytesmemory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly// solhint-disable-next-line no-inline-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Contract Source Code
File 5 of 138: AddressArrayLib.sol
// 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.
*/pragmasolidity 0.6.12;/// @title AddressArray Library/// @author Enzyme Council <security@enzyme.finance>/// @notice A library to extend the address array data typelibraryAddressArrayLib{
/// @dev Helper to verify if an array contains a particular valuefunctioncontains(address[] memory _self, address _target)
internalpurereturns (bool doesContain_)
{
for (uint256 i; i < _self.length; i++) {
if (_target == _self[i]) {
returntrue;
}
}
returnfalse;
}
/// @dev Helper to verify if array is a set of unique values./// Does not assert length > 0.functionisUniqueSet(address[] memory _self) internalpurereturns (bool isUnique_) {
if (_self.length<=1) {
returntrue;
}
uint256 arrayLength = _self.length;
for (uint256 i; i < arrayLength; i++) {
for (uint256 j = i +1; j < arrayLength; j++) {
if (_self[i] == _self[j]) {
returnfalse;
}
}
}
returntrue;
}
/// @dev Helper to remove items from an array. Removes all matching occurrences of each item./// Does not assert uniqueness of either array.functionremoveItems(address[] memory _self, address[] memory _itemsToRemove)
internalpurereturns (address[] memory nextArray_)
{
if (_itemsToRemove.length==0) {
return _self;
}
bool[] memory indexesToRemove =newbool[](_self.length);
uint256 remainingItemsCount = _self.length;
for (uint256 i; i < _self.length; i++) {
if (contains(_itemsToRemove, _self[i])) {
indexesToRemove[i] =true;
remainingItemsCount--;
}
}
if (remainingItemsCount == _self.length) {
nextArray_ = _self;
} elseif (remainingItemsCount >0) {
nextArray_ =newaddress[](remainingItemsCount);
uint256 nextArrayIndex;
for (uint256 i; i < _self.length; i++) {
if (!indexesToRemove[i]) {
nextArray_[nextArrayIndex] = _self[i];
nextArrayIndex++;
}
}
}
return nextArray_;
}
}
Contract Source Code
File 6 of 138: AddressListPolicyMixin.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/utils/EnumerableSet.sol";
/// @title AddressListPolicyMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice An abstract mixin contract for policies that use an address listabstractcontractAddressListPolicyMixin{
usingEnumerableSetforEnumerableSet.AddressSet;
eventAddressesAdded(addressindexed comptrollerProxy, address[] items);
eventAddressesRemoved(addressindexed comptrollerProxy, address[] items);
mapping(address=> EnumerableSet.AddressSet) private comptrollerProxyToList;
// EXTERNAL FUNCTIONS/// @notice Get all addresses in a fund's list/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @return list_ The addresses in the fund's listfunctiongetList(address _comptrollerProxy) externalviewreturns (address[] memory list_) {
list_ =newaddress[](comptrollerProxyToList[_comptrollerProxy].length());
for (uint256 i =0; i < list_.length; i++) {
list_[i] = comptrollerProxyToList[_comptrollerProxy].at(i);
}
return list_;
}
// PUBLIC FUNCTIONS/// @notice Check if an address is in a fund's list/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _item The address to check against the list/// @return isInList_ True if the address is in the listfunctionisInList(address _comptrollerProxy, address _item)
publicviewreturns (bool isInList_)
{
return comptrollerProxyToList[_comptrollerProxy].contains(_item);
}
// INTERNAL FUNCTIONS/// @dev Helper to add addresses to the calling fund's listfunction__addToList(address _comptrollerProxy, address[] memory _items) internal{
require(_items.length>0, "__addToList: No addresses provided");
for (uint256 i =0; i < _items.length; i++) {
require(
comptrollerProxyToList[_comptrollerProxy].add(_items[i]),
"__addToList: Address already exists in list"
);
}
emit AddressesAdded(_comptrollerProxy, _items);
}
/// @dev Helper to remove addresses from the calling fund's listfunction__removeFromList(address _comptrollerProxy, address[] memory _items) internal{
require(_items.length>0, "__removeFromList: No addresses provided");
for (uint256 i =0; i < _items.length; i++) {
require(
comptrollerProxyToList[_comptrollerProxy].remove(_items[i]),
"__removeFromList: Address does not exist in list"
);
}
emit AddressesRemoved(_comptrollerProxy, _items);
}
}
Contract Source Code
File 7 of 138: AggregatedDerivativePriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/DispatcherOwnerMixin.sol";
import"./IAggregatedDerivativePriceFeed.sol";
/// @title AggregatedDerivativePriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Aggregates multiple derivative price feeds (e.g., Compound, Chai) and dispatches/// rate requests to the appropriate feedcontractAggregatedDerivativePriceFeedisIAggregatedDerivativePriceFeed, DispatcherOwnerMixin{
eventDerivativeAdded(addressindexed derivative, address priceFeed);
eventDerivativeRemoved(addressindexed derivative);
eventDerivativeUpdated(addressindexed derivative,
address prevPriceFeed,
address nextPriceFeed
);
mapping(address=>address) private derivativeToPriceFeed;
constructor(address _dispatcher,
address[] memory _derivatives,
address[] memory _priceFeeds
) publicDispatcherOwnerMixin(_dispatcher) {
if (_derivatives.length>0) {
__addDerivatives(_derivatives, _priceFeeds);
}
}
/// @notice Gets the rates for 1 unit of the derivative to its underlying assets/// @param _derivative The derivative for which to get the rates/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The rates for the _derivative to the underlyings_functioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
address derivativePriceFeed = derivativeToPriceFeed[_derivative];
require(
derivativePriceFeed !=address(0),
"calcUnderlyingValues: _derivative is not supported"
);
return
IDerivativePriceFeed(derivativePriceFeed).calcUnderlyingValues(
_derivative,
_derivativeAmount
);
}
/// @notice Checks whether an asset is a supported derivative/// @param _asset The asset to check/// @return isSupported_ True if the asset is a supported derivative/// @dev This should be as low-cost and simple as possiblefunctionisSupportedAsset(address _asset) externalviewoverridereturns (bool isSupported_) {
return derivativeToPriceFeed[_asset] !=address(0);
}
//////////////////////////// DERIVATIVES REGISTRY /////////////////////////////// @notice Adds a list of derivatives with the given price feed values/// @param _derivatives The derivatives to add/// @param _priceFeeds The ordered price feeds corresponding to the list of _derivativesfunctionaddDerivatives(address[] calldata _derivatives, address[] calldata _priceFeeds)
externalonlyDispatcherOwner{
require(_derivatives.length>0, "addDerivatives: _derivatives cannot be empty");
__addDerivatives(_derivatives, _priceFeeds);
}
/// @notice Removes a list of derivatives/// @param _derivatives The derivatives to removefunctionremoveDerivatives(address[] calldata _derivatives) externalonlyDispatcherOwner{
require(_derivatives.length>0, "removeDerivatives: _derivatives cannot be empty");
for (uint256 i =0; i < _derivatives.length; i++) {
require(
derivativeToPriceFeed[_derivatives[i]] !=address(0),
"removeDerivatives: Derivative not yet added"
);
delete derivativeToPriceFeed[_derivatives[i]];
emit DerivativeRemoved(_derivatives[i]);
}
}
/// @notice Updates a list of derivatives with the given price feed values/// @param _derivatives The derivatives to update/// @param _priceFeeds The ordered price feeds corresponding to the list of _derivativesfunctionupdateDerivatives(address[] calldata _derivatives, address[] calldata _priceFeeds)
externalonlyDispatcherOwner{
require(_derivatives.length>0, "updateDerivatives: _derivatives cannot be empty");
require(
_derivatives.length== _priceFeeds.length,
"updateDerivatives: Unequal _derivatives and _priceFeeds array lengths"
);
for (uint256 i =0; i < _derivatives.length; i++) {
address prevPriceFeed = derivativeToPriceFeed[_derivatives[i]];
require(prevPriceFeed !=address(0), "updateDerivatives: Derivative not yet added");
require(_priceFeeds[i] != prevPriceFeed, "updateDerivatives: Value already set");
__validateDerivativePriceFeed(_derivatives[i], _priceFeeds[i]);
derivativeToPriceFeed[_derivatives[i]] = _priceFeeds[i];
emit DerivativeUpdated(_derivatives[i], prevPriceFeed, _priceFeeds[i]);
}
}
/// @dev Helper to add derivative-feed pairsfunction__addDerivatives(address[] memory _derivatives, address[] memory _priceFeeds)
private{
require(
_derivatives.length== _priceFeeds.length,
"__addDerivatives: Unequal _derivatives and _priceFeeds array lengths"
);
for (uint256 i =0; i < _derivatives.length; i++) {
require(
derivativeToPriceFeed[_derivatives[i]] ==address(0),
"__addDerivatives: Already added"
);
__validateDerivativePriceFeed(_derivatives[i], _priceFeeds[i]);
derivativeToPriceFeed[_derivatives[i]] = _priceFeeds[i];
emit DerivativeAdded(_derivatives[i], _priceFeeds[i]);
}
}
/// @dev Helper to validate a derivative price feedfunction__validateDerivativePriceFeed(address _derivative, address _priceFeed) privateview{
require(_derivative !=address(0), "__validateDerivativePriceFeed: Empty _derivative");
require(_priceFeed !=address(0), "__validateDerivativePriceFeed: Empty _priceFeed");
require(
IDerivativePriceFeed(_priceFeed).isSupportedAsset(_derivative),
"__validateDerivativePriceFeed: Unsupported derivative"
);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the registered price feed for a given derivative/// @return priceFeed_ The price feed contract addressfunctiongetPriceFeedForDerivative(address _derivative)
externalviewoverridereturns (address priceFeed_)
{
return derivativeToPriceFeed[_derivative];
}
}
Contract Source Code
File 8 of 138: AssetBlacklist.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../core/fund/comptroller/ComptrollerLib.sol";
import"../../../../core/fund/vault/VaultLib.sol";
import"../../../../utils/AddressArrayLib.sol";
import"../utils/AddressListPolicyMixin.sol";
import"./utils/PostCallOnIntegrationValidatePolicyBase.sol";
/// @title AssetBlacklist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that disallows a configurable blacklist of assets in a fund's holdingscontractAssetBlacklistisPostCallOnIntegrationValidatePolicyBase, AddressListPolicyMixin{
usingAddressArrayLibforaddress[];
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Validates and initializes a policy as necessary prior to fund activation/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _vaultProxy The fund's VaultProxy addressfunctionactivateForFund(address _comptrollerProxy, address _vaultProxy)
externaloverrideonlyPolicyManager{
require(
passesRule(_comptrollerProxy, VaultLib(_vaultProxy).getTrackedAssets()),
"activateForFund: Blacklisted asset detected"
);
}
/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
address[] memory assets =abi.decode(_encodedSettings, (address[]));
require(
!assets.contains(ComptrollerLib(_comptrollerProxy).getDenominationAsset()),
"addFundSettings: Cannot blacklist denominationAsset"
);
__addToList(_comptrollerProxy, assets);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ASSET_BLACKLIST";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _assets The assets with which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address[] memory _assets)
publicviewreturns (bool isValid_)
{
for (uint256 i; i < _assets.length; i++) {
if (isInList(_comptrollerProxy, _assets[i])) {
returnfalse;
}
}
returntrue;
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(, , address[] memory incomingAssets, , , ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, incomingAssets);
}
}
Contract Source Code
File 9 of 138: AssetFinalityResolver.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../infrastructure/price-feeds/derivatives/feeds/SynthetixPriceFeed.sol";
import"../interfaces/ISynthetixAddressResolver.sol";
import"../interfaces/ISynthetixExchanger.sol";
/// @title AssetFinalityResolver Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A contract that helps achieve asset finalityabstractcontractAssetFinalityResolver{
addressinternalimmutable SYNTHETIX_ADDRESS_RESOLVER;
addressinternalimmutable SYNTHETIX_PRICE_FEED;
constructor(address _synthetixPriceFeed, address _synthetixAddressResolver) public{
SYNTHETIX_ADDRESS_RESOLVER = _synthetixAddressResolver;
SYNTHETIX_PRICE_FEED = _synthetixPriceFeed;
}
/// @dev Helper to finalize a Synth balance at a given target address and return its balancefunction__finalizeIfSynthAndGetAssetBalance(address _target,
address _asset,
bool _requireFinality
) internalreturns (uint256 assetBalance_) {
bytes32 currencyKey = SynthetixPriceFeed(SYNTHETIX_PRICE_FEED).getCurrencyKeyForSynth(
_asset
);
if (currencyKey !=0) {
address synthetixExchanger = ISynthetixAddressResolver(SYNTHETIX_ADDRESS_RESOLVER)
.requireAndGetAddress(
"Exchanger",
"finalizeAndGetAssetBalance: Missing Exchanger"
);
try ISynthetixExchanger(synthetixExchanger).settle(_target, currencyKey) {} catch {
require(!_requireFinality, "finalizeAndGetAssetBalance: Cannot settle Synth");
}
}
return ERC20(_asset).balanceOf(_target);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `SYNTHETIX_ADDRESS_RESOLVER` variable/// @return synthetixAddressResolver_ The `SYNTHETIX_ADDRESS_RESOLVER` variable valuefunctiongetSynthetixAddressResolver()
externalviewreturns (address synthetixAddressResolver_)
{
return SYNTHETIX_ADDRESS_RESOLVER;
}
/// @notice Gets the `SYNTHETIX_PRICE_FEED` variable/// @return synthetixPriceFeed_ The `SYNTHETIX_PRICE_FEED` variable valuefunctiongetSynthetixPriceFeed() externalviewreturns (address synthetixPriceFeed_) {
return SYNTHETIX_PRICE_FEED;
}
}
Contract Source Code
File 10 of 138: AssetWhitelist.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../core/fund/comptroller/ComptrollerLib.sol";
import"../../../../core/fund/vault/VaultLib.sol";
import"../../../../utils/AddressArrayLib.sol";
import"../utils/AddressListPolicyMixin.sol";
import"./utils/PostCallOnIntegrationValidatePolicyBase.sol";
/// @title AssetWhitelist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that only allows a configurable whitelist of assets in a fund's holdingscontractAssetWhitelistisPostCallOnIntegrationValidatePolicyBase, AddressListPolicyMixin{
usingAddressArrayLibforaddress[];
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Validates and initializes a policy as necessary prior to fund activation/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _vaultProxy The fund's VaultProxy addressfunctionactivateForFund(address _comptrollerProxy, address _vaultProxy)
externaloverrideonlyPolicyManager{
require(
passesRule(_comptrollerProxy, VaultLib(_vaultProxy).getTrackedAssets()),
"activateForFund: Non-whitelisted asset detected"
);
}
/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
address[] memory assets =abi.decode(_encodedSettings, (address[]));
require(
assets.contains(ComptrollerLib(_comptrollerProxy).getDenominationAsset()),
"addFundSettings: Must whitelist denominationAsset"
);
__addToList(_comptrollerProxy, abi.decode(_encodedSettings, (address[])));
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ASSET_WHITELIST";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _assets The assets with which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address[] memory _assets)
publicviewreturns (bool isValid_)
{
for (uint256 i; i < _assets.length; i++) {
if (!isInList(_comptrollerProxy, _assets[i])) {
returnfalse;
}
}
returntrue;
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(, , address[] memory incomingAssets, , , ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, incomingAssets);
}
}
Contract Source Code
File 11 of 138: AuthUserExecutedSharesRequestorFactory.sol
// 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.
*/pragmasolidity 0.6.12;import"../../core/fund/comptroller/ComptrollerLib.sol";
import"../../core/fund/vault/VaultLib.sol";
import"./AuthUserExecutedSharesRequestorProxy.sol";
import"./IAuthUserExecutedSharesRequestor.sol";
/// @title AuthUserExecutedSharesRequestorFactory Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Deploys and maintains a record of AuthUserExecutedSharesRequestorProxy instancescontractAuthUserExecutedSharesRequestorFactory{
eventSharesRequestorProxyDeployed(addressindexed comptrollerProxy,
address sharesRequestorProxy
);
addressprivateimmutable AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB;
addressprivateimmutable DISPATCHER;
mapping(address=>address) private comptrollerProxyToSharesRequestorProxy;
constructor(address _dispatcher, address _authUserExecutedSharesRequestorLib) public{
AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB = _authUserExecutedSharesRequestorLib;
DISPATCHER = _dispatcher;
}
/// @notice Deploys a shares requestor proxy instance for a given ComptrollerProxy instance/// @param _comptrollerProxy The ComptrollerProxy for which to deploy the shares requestor proxy/// @return sharesRequestorProxy_ The address of the newly-deployed shares requestor proxyfunctiondeploySharesRequestorProxy(address _comptrollerProxy)
externalreturns (address sharesRequestorProxy_)
{
// Confirm fund is genuine
VaultLib vaultProxyContract = VaultLib(ComptrollerLib(_comptrollerProxy).getVaultProxy());
require(
vaultProxyContract.getAccessor() == _comptrollerProxy,
"deploySharesRequestorProxy: Invalid VaultProxy for ComptrollerProxy"
);
require(
IDispatcher(DISPATCHER).getFundDeployerForVaultProxy(address(vaultProxyContract)) !=address(0),
"deploySharesRequestorProxy: Not a genuine fund"
);
// Validate that the caller is the fund ownerrequire(
msg.sender== vaultProxyContract.getOwner(),
"deploySharesRequestorProxy: Only fund owner callable"
);
// Validate that a proxy does not already existrequire(
comptrollerProxyToSharesRequestorProxy[_comptrollerProxy] ==address(0),
"deploySharesRequestorProxy: Proxy already exists"
);
// Deploy the proxybytesmemory constructData =abi.encodeWithSelector(
IAuthUserExecutedSharesRequestor.init.selector,
_comptrollerProxy
);
sharesRequestorProxy_ =address(
new AuthUserExecutedSharesRequestorProxy(
constructData,
AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB
)
);
comptrollerProxyToSharesRequestorProxy[_comptrollerProxy] = sharesRequestorProxy_;
emit SharesRequestorProxyDeployed(_comptrollerProxy, sharesRequestorProxy_);
return sharesRequestorProxy_;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the value of the `AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB` variable/// @return authUserExecutedSharesRequestorLib_ The `AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB` variable valuefunctiongetAuthUserExecutedSharesRequestorLib()
externalviewreturns (address authUserExecutedSharesRequestorLib_)
{
return AUTH_USER_EXECUTED_SHARES_REQUESTOR_LIB;
}
/// @notice Gets the value of the `DISPATCHER` variable/// @return dispatcher_ The `DISPATCHER` variable valuefunctiongetDispatcher() externalviewreturns (address dispatcher_) {
return DISPATCHER;
}
/// @notice Gets the AuthUserExecutedSharesRequestorProxy associated with the given ComptrollerProxy/// @param _comptrollerProxy The ComptrollerProxy for which to get the associated AuthUserExecutedSharesRequestorProxy/// @return sharesRequestorProxy_ The associated AuthUserExecutedSharesRequestorProxy addressfunctiongetSharesRequestorProxyForComptrollerProxy(address _comptrollerProxy)
externalviewreturns (address sharesRequestorProxy_)
{
return comptrollerProxyToSharesRequestorProxy[_comptrollerProxy];
}
}
Contract Source Code
File 12 of 138: AuthUserExecutedSharesRequestorLib.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"../../core/fund/comptroller/ComptrollerLib.sol";
import"../../core/fund/vault/VaultLib.sol";
import"./IAuthUserExecutedSharesRequestor.sol";
/// @title AuthUserExecutedSharesRequestorLib Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Provides the logic for AuthUserExecutedSharesRequestorProxy instances,/// in which shares requests are manually executed by a permissioned user/// @dev This will not work with a `denominationAsset` that does not transfer/// the exact expected amount or has an elastic supply.contractAuthUserExecutedSharesRequestorLibisIAuthUserExecutedSharesRequestor{
usingSafeERC20forERC20;
usingSafeMathforuint256;
eventRequestCanceled(addressindexed requestOwner,
uint256 investmentAmount,
uint256 minSharesQuantity
);
eventRequestCreated(addressindexed requestOwner,
uint256 investmentAmount,
uint256 minSharesQuantity
);
eventRequestExecuted(addressindexed caller,
addressindexed requestOwner,
uint256 investmentAmount,
uint256 minSharesQuantity
);
eventRequestExecutorAdded(addressindexed account);
eventRequestExecutorRemoved(addressindexed account);
structRequestInfo {
uint256 investmentAmount;
uint256 minSharesQuantity;
}
uint256privateconstant CANCELLATION_COOLDOWN_TIMELOCK =10minutes;
addressprivate comptrollerProxy;
addressprivate denominationAsset;
addressprivate fundOwner;
mapping(address=> RequestInfo) private ownerToRequestInfo;
mapping(address=>bool) private acctToIsRequestExecutor;
mapping(address=>uint256) private ownerToLastRequestCancellation;
modifieronlyFundOwner() {
require(msg.sender== fundOwner, "Only fund owner callable");
_;
}
/// @notice Initializes a proxy instance that uses this library/// @dev Serves as a per-proxy pseudo-constructorfunctioninit(address _comptrollerProxy) externaloverride{
require(comptrollerProxy ==address(0), "init: Already initialized");
comptrollerProxy = _comptrollerProxy;
// Cache frequently-used values that require external calls
ComptrollerLib comptrollerProxyContract = ComptrollerLib(_comptrollerProxy);
denominationAsset = comptrollerProxyContract.getDenominationAsset();
fundOwner = VaultLib(comptrollerProxyContract.getVaultProxy()).getOwner();
}
/// @notice Cancels the shares request of the callerfunctioncancelRequest() external{
RequestInfo memory request = ownerToRequestInfo[msg.sender];
require(request.investmentAmount >0, "cancelRequest: Request does not exist");
// Delete the request, start the cooldown period, and return the investment assetdelete ownerToRequestInfo[msg.sender];
ownerToLastRequestCancellation[msg.sender] =block.timestamp;
ERC20(denominationAsset).safeTransfer(msg.sender, request.investmentAmount);
emit RequestCanceled(msg.sender, request.investmentAmount, request.minSharesQuantity);
}
/// @notice Creates a shares request for the caller/// @param _investmentAmount The amount of the fund's denomination asset to use to buy shares/// @param _minSharesQuantity The minimum quantity of shares to buy with the _investmentAmountfunctioncreateRequest(uint256 _investmentAmount, uint256 _minSharesQuantity) external{
require(_investmentAmount >0, "createRequest: _investmentAmount must be > 0");
require(
ownerToRequestInfo[msg.sender].investmentAmount ==0,
"createRequest: The request owner can only create one request before executed or canceled"
);
require(
ownerToLastRequestCancellation[msg.sender] <block.timestamp.sub(CANCELLATION_COOLDOWN_TIMELOCK),
"createRequest: Cannot create request during cancellation cooldown period"
);
// Create the Request and take custody of investment asset
ownerToRequestInfo[msg.sender] = RequestInfo({
investmentAmount: _investmentAmount,
minSharesQuantity: _minSharesQuantity
});
ERC20(denominationAsset).safeTransferFrom(msg.sender, address(this), _investmentAmount);
emit RequestCreated(msg.sender, _investmentAmount, _minSharesQuantity);
}
/// @notice Executes multiple shares requests/// @param _requestOwners The owners of the pending shares requestsfunctionexecuteRequests(address[] calldata _requestOwners) external{
require(
msg.sender== fundOwner || isRequestExecutor(msg.sender),
"executeRequests: Invalid caller"
);
require(_requestOwners.length>0, "executeRequests: _requestOwners can not be empty");
(
address[] memory buyers,
uint256[] memory investmentAmounts,
uint256[] memory minSharesQuantities,
uint256 totalInvestmentAmount
) = __convertRequestsToBuySharesParams(_requestOwners);
// Since ComptrollerProxy instances are fully trusted,// we can approve them with the max amount of the denomination asset,// and only top the approval back to max if ever necessary.address comptrollerProxyCopy = comptrollerProxy;
ERC20 denominationAssetContract = ERC20(denominationAsset);
if (
denominationAssetContract.allowance(address(this), comptrollerProxyCopy) <
totalInvestmentAmount
) {
denominationAssetContract.approve(comptrollerProxyCopy, type(uint256).max);
}
ComptrollerLib(comptrollerProxyCopy).buyShares(
buyers,
investmentAmounts,
minSharesQuantities
);
}
/// @dev Helper to convert raw shares requests into the format required by buyShares()./// It also removes any empty requests, which is necessary to prevent a DoS attack where a user/// cancels their request earlier in the same block (can be repeated from multiple accounts)./// This function also removes shares requests and fires success events as it loops through them.function__convertRequestsToBuySharesParams(address[] memory _requestOwners)
privatereturns (address[] memory buyers_,
uint256[] memory investmentAmounts_,
uint256[] memory minSharesQuantities_,
uint256 totalInvestmentAmount_
)
{
uint256 existingRequestsCount = _requestOwners.length;
uint256[] memory allInvestmentAmounts =newuint256[](_requestOwners.length);
// Loop through once to get the count of existing requestsfor (uint256 i; i < _requestOwners.length; i++) {
allInvestmentAmounts[i] = ownerToRequestInfo[_requestOwners[i]].investmentAmount;
if (allInvestmentAmounts[i] ==0) {
existingRequestsCount--;
}
}
// Loop through a second time to format requests for buyShares(),// and to delete the requests and emit events early so no further looping is needed.
buyers_ =newaddress[](existingRequestsCount);
investmentAmounts_ =newuint256[](existingRequestsCount);
minSharesQuantities_ =newuint256[](existingRequestsCount);
uint256 existingRequestsIndex;
for (uint256 i; i < _requestOwners.length; i++) {
if (allInvestmentAmounts[i] ==0) {
continue;
}
buyers_[existingRequestsIndex] = _requestOwners[i];
investmentAmounts_[existingRequestsIndex] = allInvestmentAmounts[i];
minSharesQuantities_[existingRequestsIndex] = ownerToRequestInfo[_requestOwners[i]]
.minSharesQuantity;
totalInvestmentAmount_ = totalInvestmentAmount_.add(allInvestmentAmounts[i]);
delete ownerToRequestInfo[_requestOwners[i]];
emit RequestExecuted(
msg.sender,
buyers_[existingRequestsIndex],
investmentAmounts_[existingRequestsIndex],
minSharesQuantities_[existingRequestsIndex]
);
existingRequestsIndex++;
}
return (buyers_, investmentAmounts_, minSharesQuantities_, totalInvestmentAmount_);
}
///////////////////////////////// REQUEST EXECUTOR REGISTRY //////////////////////////////////// @notice Adds accounts to request executors/// @param _requestExecutors Accounts to addfunctionaddRequestExecutors(address[] calldata _requestExecutors) externalonlyFundOwner{
require(_requestExecutors.length>0, "addRequestExecutors: Empty _requestExecutors");
for (uint256 i; i < _requestExecutors.length; i++) {
require(
!isRequestExecutor(_requestExecutors[i]),
"addRequestExecutors: Value already set"
);
require(
_requestExecutors[i] != fundOwner,
"addRequestExecutors: The fund owner cannot be added"
);
acctToIsRequestExecutor[_requestExecutors[i]] =true;
emit RequestExecutorAdded(_requestExecutors[i]);
}
}
/// @notice Removes accounts from request executors/// @param _requestExecutors Accounts to removefunctionremoveRequestExecutors(address[] calldata _requestExecutors) externalonlyFundOwner{
require(_requestExecutors.length>0, "removeRequestExecutors: Empty _requestExecutors");
for (uint256 i; i < _requestExecutors.length; i++) {
require(
isRequestExecutor(_requestExecutors[i]),
"removeRequestExecutors: Account is not a request executor"
);
acctToIsRequestExecutor[_requestExecutors[i]] =false;
emit RequestExecutorRemoved(_requestExecutors[i]);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the value of `comptrollerProxy` variable/// @return comptrollerProxy_ The `comptrollerProxy` variable valuefunctiongetComptrollerProxy() externalviewreturns (address comptrollerProxy_) {
return comptrollerProxy;
}
/// @notice Gets the value of `denominationAsset` variable/// @return denominationAsset_ The `denominationAsset` variable valuefunctiongetDenominationAsset() externalviewreturns (address denominationAsset_) {
return denominationAsset;
}
/// @notice Gets the value of `fundOwner` variable/// @return fundOwner_ The `fundOwner` variable valuefunctiongetFundOwner() externalviewreturns (address fundOwner_) {
return fundOwner;
}
/// @notice Gets the request info of a user/// @param _requestOwner The address of the user that creates the request/// @return requestInfo_ The request info created by the userfunctiongetSharesRequestInfoForOwner(address _requestOwner)
externalviewreturns (RequestInfo memory requestInfo_)
{
return ownerToRequestInfo[_requestOwner];
}
/// @notice Checks whether an account is a request executor/// @param _who The account to check/// @return isRequestExecutor_ True if _who is a request executorfunctionisRequestExecutor(address _who) publicviewreturns (bool isRequestExecutor_) {
return acctToIsRequestExecutor[_who];
}
}
Contract Source Code
File 13 of 138: AuthUserExecutedSharesRequestorProxy.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/Proxy.sol";
contractAuthUserExecutedSharesRequestorProxyisProxy{
constructor(bytesmemory _constructData, address _authUserExecutedSharesRequestorLib)
publicProxy(_constructData, _authUserExecutedSharesRequestorLib)
{}
}
Contract Source Code
File 14 of 138: BuySharesCallerWhitelist.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/AddressListPolicyMixin.sol";
import"./utils/BuySharesSetupPolicyBase.sol";
/// @title BuySharesCallerWhitelist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that only allows a configurable whitelist of buyShares callers for a fundcontractBuySharesCallerWhitelistisBuySharesSetupPolicyBase, AddressListPolicyMixin{
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Adds the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
__updateList(_comptrollerProxy, _encodedSettings);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"BUY_SHARES_CALLER_WHITELIST";
}
/// @notice Updates the policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionupdateFundSettings(address _comptrollerProxy,
address,
bytescalldata _encodedSettings
) externaloverrideonlyPolicyManager{
__updateList(_comptrollerProxy, _encodedSettings);
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _buySharesCaller The buyShares caller for which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address _buySharesCaller)
publicviewreturns (bool isValid_)
{
return isInList(_comptrollerProxy, _buySharesCaller);
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(address caller, , ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, caller);
}
/// @dev Helper to update the whitelist by adding and/or removing addressesfunction__updateList(address _comptrollerProxy, bytesmemory _settingsData) private{
(address[] memory itemsToAdd, address[] memory itemsToRemove) =abi.decode(
_settingsData,
(address[], address[])
);
// If an address is in both add and remove arrays, they will not be in the final list.// We do not check for uniqueness between the two arrays for efficiency.if (itemsToAdd.length>0) {
__addToList(_comptrollerProxy, itemsToAdd);
}
if (itemsToRemove.length>0) {
__removeFromList(_comptrollerProxy, itemsToRemove);
}
}
}
Contract Source Code
File 15 of 138: BuySharesSetupPolicyBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/PolicyBase.sol";
/// @title BuySharesSetupPolicyBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract for policies that only implement the BuySharesSetup policy hookabstractcontractBuySharesSetupPolicyBaseisPolicyBase{
/// @notice Gets the implemented PolicyHooks for a policy/// @return implementedHooks_ The implemented PolicyHooksfunctionimplementedHooks()
externalviewoverridereturns (IPolicyManager.PolicyHook[] memory implementedHooks_)
{
implementedHooks_ =new IPolicyManager.PolicyHook[](1);
implementedHooks_[0] = IPolicyManager.PolicyHook.BuySharesSetup;
return implementedHooks_;
}
/// @notice Helper to decode rule argumentsfunction__decodeRuleArgs(bytesmemory _encodedArgs)
internalpurereturns (address caller_,
uint256[] memory investmentAmounts_,
uint256 gav_
)
{
returnabi.decode(_encodedArgs, (address, uint256[], uint256));
}
}
Contract Source Code
File 16 of 138: CentralizedRateProvider.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../release/infrastructure/value-interpreter/IValueInterpreter.sol";
import"../../release/infrastructure/price-feeds/derivatives/IAggregatedDerivativePriceFeed.sol";
import"../../release/infrastructure/price-feeds/primitives/IPrimitivePriceFeed.sol";
/// @dev This contract acts as a centralized rate provider for mocks./// Suited for a dev environment, it doesn't take into account gas costs.contractCentralizedRateProviderisOwnable{
usingSafeMathforuint256;
addressprivateimmutable WETH;
uint256private maxDeviationPerSender;
// Addresses are not immutable to facilitate lazy load (they're are not accessible at the mock env).addressprivate valueInterpreter;
addressprivate aggregateDerivativePriceFeed;
addressprivate primitivePriceFeed;
constructor(address _weth, uint256 _maxDeviationPerSender) public{
maxDeviationPerSender = _maxDeviationPerSender;
WETH = _weth;
}
/// @dev Calculates the value of a _baseAsset relative to a _quoteAsset./// Label to ValueInterprete's calcLiveAssetValuefunctioncalcLiveAssetValue(address _baseAsset,
uint256 _amount,
address _quoteAsset
) publicreturns (uint256 value_) {
uint256 baseDecimalsRate =10**uint256(ERC20(_baseAsset).decimals());
uint256 quoteDecimalsRate =10**uint256(ERC20(_quoteAsset).decimals());
// 1. Check if quote asset is a primitive. If it is, use ValueInterpreter normally.if (IPrimitivePriceFeed(primitivePriceFeed).isSupportedAsset(_quoteAsset)) {
(value_, ) = IValueInterpreter(valueInterpreter).calcLiveAssetValue(
_baseAsset,
_amount,
_quoteAsset
);
return value_;
}
// 2. Otherwise, check if base asset is a primitive, and use inverse rate from Value Interpreter.if (IPrimitivePriceFeed(primitivePriceFeed).isSupportedAsset(_baseAsset)) {
(uint256 inverseRate, ) = IValueInterpreter(valueInterpreter).calcLiveAssetValue(
_quoteAsset,
10**uint256(ERC20(_quoteAsset).decimals()),
_baseAsset
);
uint256 rate =uint256(baseDecimalsRate).mul(quoteDecimalsRate).div(inverseRate);
value_ = _amount.mul(rate).div(baseDecimalsRate);
return value_;
}
// 3. If both assets are derivatives, calculate the rate against ETH.
(uint256 baseToWeth, ) = IValueInterpreter(valueInterpreter).calcLiveAssetValue(
_baseAsset,
baseDecimalsRate,
WETH
);
(uint256 quoteToWeth, ) = IValueInterpreter(valueInterpreter).calcLiveAssetValue(
_quoteAsset,
quoteDecimalsRate,
WETH
);
value_ = _amount.mul(baseToWeth).mul(quoteDecimalsRate).div(quoteToWeth).div(
baseDecimalsRate
);
return value_;
}
/// @dev Calculates a randomized live value of an asset/// Aggregation of two randomization seeds: msg.sender, and by block.number.functioncalcLiveAssetValueRandomized(address _baseAsset,
uint256 _amount,
address _quoteAsset,
uint256 _maxDeviationPerBlock
) externalreturns (uint256 value_) {
uint256 liveAssetValue = calcLiveAssetValue(_baseAsset, _amount, _quoteAsset);
// Range [liveAssetValue * (1 - _blockNumberDeviation), liveAssetValue * (1 + _blockNumberDeviation)]uint256 senderRandomizedValue_ = __calcValueRandomizedByAddress(
liveAssetValue,
msg.sender,
maxDeviationPerSender
);
// Range [liveAssetValue * (1 - _maxDeviationPerBlock - maxDeviationPerSender), liveAssetValue * (1 + _maxDeviationPerBlock + maxDeviationPerSender)]
value_ = __calcValueRandomizedByUint(
senderRandomizedValue_,
block.number,
_maxDeviationPerBlock
);
return value_;
}
/// @dev Calculates the live value of an asset including a grade of pseudo randomization, using msg.sender as the source of randomnessfunctioncalcLiveAssetValueRandomizedByBlockNumber(address _baseAsset,
uint256 _amount,
address _quoteAsset,
uint256 _maxDeviationPerBlock
) externalreturns (uint256 value_) {
uint256 liveAssetValue = calcLiveAssetValue(_baseAsset, _amount, _quoteAsset);
value_ = __calcValueRandomizedByUint(liveAssetValue, block.number, _maxDeviationPerBlock);
return value_;
}
/// @dev Calculates the live value of an asset including a grade of pseudo-randomization, using `block.number` as the source of randomnessfunctioncalcLiveAssetValueRandomizedBySender(address _baseAsset,
uint256 _amount,
address _quoteAsset
) externalreturns (uint256 value_) {
uint256 liveAssetValue = calcLiveAssetValue(_baseAsset, _amount, _quoteAsset);
value_ = __calcValueRandomizedByAddress(liveAssetValue, msg.sender, maxDeviationPerSender);
return value_;
}
functionsetMaxDeviationPerSender(uint256 _maxDeviationPerSender) externalonlyOwner{
maxDeviationPerSender = _maxDeviationPerSender;
}
/// @dev Connector from release environment, inject price variables into the provider.functionsetReleasePriceAddresses(address _valueInterpreter,
address _aggregateDerivativePriceFeed,
address _primitivePriceFeed
) externalonlyOwner{
valueInterpreter = _valueInterpreter;
aggregateDerivativePriceFeed = _aggregateDerivativePriceFeed;
primitivePriceFeed = _primitivePriceFeed;
}
// PRIVATE FUNCTIONS/// @dev Calculates a a pseudo-randomized value as a seed an addressfunction__calcValueRandomizedByAddress(uint256 _meanValue,
address _seed,
uint256 _maxDeviation
) privatepurereturns (uint256 value_) {
// Value between [0, 100]uint256 senderRandomFactor =uint256(uint8(_seed))
.mul(100)
.div(256)
.mul(_maxDeviation)
.div(100);
value_ = __calcDeviatedValue(_meanValue, senderRandomFactor, _maxDeviation);
return value_;
}
/// @dev Calculates a a pseudo-randomized value as a seed an uint256function__calcValueRandomizedByUint(uint256 _meanValue,
uint256 _seed,
uint256 _maxDeviation
) privatepurereturns (uint256 value_) {
// Depending on the _seed number, it will be one of {20, 40, 60, 80, 100}uint256 randomFactor = (_seed.mod(2).mul(20))
.add((_seed.mod(3).mul(40)))
.mul(_maxDeviation)
.div(100);
value_ = __calcDeviatedValue(_meanValue, randomFactor, _maxDeviation);
return value_;
}
/// @dev Given a mean value and a max deviation, returns a value in the spectrum between 0 (_meanValue - maxDeviation) and 100 (_mean + maxDeviation)/// TODO: Refactor to use 18 decimal precisionfunction__calcDeviatedValue(uint256 _meanValue,
uint256 _offset,
uint256 _maxDeviation
) privatepurereturns (uint256 value_) {
return
_meanValue.add((_meanValue.mul((uint256(2)).mul(_offset)).div(uint256(100)))).sub(
_meanValue.mul(_maxDeviation).div(uint256(100))
);
}
///////////////////// STATE GETTERS /////////////////////functiongetMaxDeviationPerSender() publicviewreturns (uint256 maxDeviationPerSender_) {
return maxDeviationPerSender;
}
functiongetValueInterpreter() publicviewreturns (address valueInterpreter_) {
return valueInterpreter;
}
}
Contract Source Code
File 17 of 138: ChaiAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../interfaces/IChai.sol";
import"../utils/AdapterBase.sol";
/// @title ChaiAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for Chai <https://github.com/dapphub/chai>contractChaiAdapterisAdapterBase{
addressprivateimmutable CHAI;
addressprivateimmutable DAI;
constructor(address _integrationManager,
address _chai,
address _dai
) publicAdapterBase(_integrationManager) {
CHAI = _chai;
DAI = _dai;
}
/// @notice Provides a constant string identifier for an adapter/// @return identifier_ An identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"CHAI";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
if (_selector == LEND_SELECTOR) {
(uint256 daiAmount, uint256 minChaiAmount) = __decodeCallArgs(_encodedCallArgs);
spendAssets_ =newaddress[](1);
spendAssets_[0] = DAI;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = daiAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = CHAI;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minChaiAmount;
} elseif (_selector == REDEEM_SELECTOR) {
(uint256 chaiAmount, uint256 minDaiAmount) = __decodeCallArgs(_encodedCallArgs);
spendAssets_ =newaddress[](1);
spendAssets_[0] = CHAI;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = chaiAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = DAI;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minDaiAmount;
} else {
revert("parseAssetsForMethod: _selector invalid");
}
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Lend Dai for Chai/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionlend(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(uint256 daiAmount, ) = __decodeCallArgs(_encodedCallArgs);
__approveMaxAsNeeded(DAI, CHAI, daiAmount);
// Execute Lend on Chai// Chai.join allows specifying the vaultProxy as the destination of Chai tokens
IChai(CHAI).join(_vaultProxy, daiAmount);
}
/// @notice Redeem Chai for Dai/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionredeem(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(uint256 chaiAmount, ) = __decodeCallArgs(_encodedCallArgs);
// Execute redeem on Chai// Chai.exit sends Dai back to the adapter
IChai(CHAI).exit(address(this), chaiAmount);
}
// PRIVATE FUNCTIONS/// @dev Helper to decode the encoded call argumentsfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (uint256 outgoingAmount_, uint256 minIncomingAmount_)
{
returnabi.decode(_encodedCallArgs, (uint256, uint256));
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `CHAI` variable value/// @return chai_ The `CHAI` variable valuefunctiongetChai() externalviewreturns (address chai_) {
return CHAI;
}
/// @notice Gets the `DAI` variable value/// @return dai_ The `DAI` variable valuefunctiongetDai() externalviewreturns (address dai_) {
return DAI;
}
}
Contract Source Code
File 18 of 138: ChaiPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/IMakerDaoPot.sol";
import"../IDerivativePriceFeed.sol";
/// @title ChaiPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Price source oracle for ChaicontractChaiPriceFeedisIDerivativePriceFeed{
usingSafeMathforuint256;
uint256privateconstant CHI_DIVISOR =10**27;
addressprivateimmutable CHAI;
addressprivateimmutable DAI;
addressprivateimmutable DSR_POT;
constructor(address _chai,
address _dai,
address _dsrPot
) public{
CHAI = _chai;
DAI = _dai;
DSR_POT = _dsrPot;
}
/// @notice Converts a given amount of a derivative to its underlying asset values/// @param _derivative The derivative to convert/// @param _derivativeAmount The amount of the derivative to convert/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amount/// @dev Calculation based on Chai source: https://github.com/dapphub/chai/blob/master/src/chai.solfunctioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
require(isSupportedAsset(_derivative), "calcUnderlyingValues: Only Chai is supported");
underlyings_ =newaddress[](1);
underlyings_[0] = DAI;
underlyingAmounts_ =newuint256[](1);
underlyingAmounts_[0] = _derivativeAmount.mul(IMakerDaoPot(DSR_POT).chi()).div(
CHI_DIVISOR
);
}
/// @notice Checks if an asset is supported by the price feed/// @param _asset The asset to check/// @return isSupported_ True if the asset is supportedfunctionisSupportedAsset(address _asset) publicviewoverridereturns (bool isSupported_) {
return _asset == CHAI;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `CHAI` variable value/// @return chai_ The `CHAI` variable valuefunctiongetChai() externalviewreturns (address chai_) {
return CHAI;
}
/// @notice Gets the `DAI` variable value/// @return dai_ The `DAI` variable valuefunctiongetDai() externalviewreturns (address dai_) {
return DAI;
}
/// @notice Gets the `DSR_POT` variable value/// @return dsrPot_ The `DSR_POT` variable valuefunctiongetDsrPot() externalviewreturns (address dsrPot_) {
return DSR_POT;
}
}
Contract Source Code
File 19 of 138: ChainlinkPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../../interfaces/IChainlinkAggregator.sol";
import"../../utils/DispatcherOwnerMixin.sol";
import"./IPrimitivePriceFeed.sol";
/// @title ChainlinkPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A price feed that uses Chainlink oracles as price sourcescontractChainlinkPriceFeedisIPrimitivePriceFeed, DispatcherOwnerMixin{
usingSafeMathforuint256;
eventEthUsdAggregatorSet(address prevEthUsdAggregator, address nextEthUsdAggregator);
eventPrimitiveAdded(addressindexed primitive,
address aggregator,
RateAsset rateAsset,
uint256 unit
);
eventPrimitiveRemoved(addressindexed primitive);
eventPrimitiveUpdated(addressindexed primitive,
address prevAggregator,
address nextAggregator
);
eventStalePrimitiveRemoved(addressindexed primitive);
eventStaleRateThresholdSet(uint256 prevStaleRateThreshold, uint256 nextStaleRateThreshold);
enumRateAsset {ETH, USD}
structAggregatorInfo {
address aggregator;
RateAsset rateAsset;
}
uint256privateconstant ETH_UNIT =10**18;
addressprivateimmutable WETH_TOKEN;
addressprivate ethUsdAggregator;
uint256private staleRateThreshold;
mapping(address=> AggregatorInfo) private primitiveToAggregatorInfo;
mapping(address=>uint256) private primitiveToUnit;
constructor(address _dispatcher,
address _wethToken,
address _ethUsdAggregator,
address[] memory _primitives,
address[] memory _aggregators,
RateAsset[] memory _rateAssets
) publicDispatcherOwnerMixin(_dispatcher) {
WETH_TOKEN = _wethToken;
staleRateThreshold =25hours; // 24 hour heartbeat + 1hr buffer
__setEthUsdAggregator(_ethUsdAggregator);
if (_primitives.length>0) {
__addPrimitives(_primitives, _aggregators, _rateAssets);
}
}
// EXTERNAL FUNCTIONS/// @notice Calculates the value of a base asset in terms of a quote asset (using a canonical rate)/// @param _baseAsset The base asset/// @param _baseAssetAmount The base asset amount to convert/// @param _quoteAsset The quote asset/// @return quoteAssetAmount_ The equivalent quote asset amount/// @return isValid_ True if the rates used in calculations are deemed validfunctioncalcCanonicalValue(address _baseAsset,
uint256 _baseAssetAmount,
address _quoteAsset
) publicviewoverridereturns (uint256 quoteAssetAmount_, bool isValid_) {
// Case where _baseAsset == _quoteAsset is handled by ValueInterpreterint256 baseAssetRate = __getLatestRateData(_baseAsset);
if (baseAssetRate <=0) {
return (0, false);
}
int256 quoteAssetRate = __getLatestRateData(_quoteAsset);
if (quoteAssetRate <=0) {
return (0, false);
}
(quoteAssetAmount_, isValid_) = __calcConversionAmount(
_baseAsset,
_baseAssetAmount,
uint256(baseAssetRate),
_quoteAsset,
uint256(quoteAssetRate)
);
return (quoteAssetAmount_, isValid_);
}
/// @notice Calculates the value of a base asset in terms of a quote asset (using a live rate)/// @param _baseAsset The base asset/// @param _baseAssetAmount The base asset amount to convert/// @param _quoteAsset The quote asset/// @return quoteAssetAmount_ The equivalent quote asset amount/// @return isValid_ True if the rates used in calculations are deemed valid/// @dev Live and canonical values are the same for ChainlinkfunctioncalcLiveValue(address _baseAsset,
uint256 _baseAssetAmount,
address _quoteAsset
) externalviewoverridereturns (uint256 quoteAssetAmount_, bool isValid_) {
return calcCanonicalValue(_baseAsset, _baseAssetAmount, _quoteAsset);
}
/// @notice Checks whether an asset is a supported primitive of the price feed/// @param _asset The asset to check/// @return isSupported_ True if the asset is a supported primitivefunctionisSupportedAsset(address _asset) externalviewoverridereturns (bool isSupported_) {
return _asset == WETH_TOKEN || primitiveToAggregatorInfo[_asset].aggregator !=address(0);
}
/// @notice Sets the `ehUsdAggregator` variable value/// @param _nextEthUsdAggregator The `ehUsdAggregator` value to setfunctionsetEthUsdAggregator(address _nextEthUsdAggregator) externalonlyDispatcherOwner{
__setEthUsdAggregator(_nextEthUsdAggregator);
}
// PRIVATE FUNCTIONS/// @dev Helper to convert an amount from a _baseAsset to a _quoteAssetfunction__calcConversionAmount(address _baseAsset,
uint256 _baseAssetAmount,
uint256 _baseAssetRate,
address _quoteAsset,
uint256 _quoteAssetRate
) privateviewreturns (uint256 quoteAssetAmount_, bool isValid_) {
RateAsset baseAssetRateAsset = getRateAssetForPrimitive(_baseAsset);
RateAsset quoteAssetRateAsset = getRateAssetForPrimitive(_quoteAsset);
uint256 baseAssetUnit = getUnitForPrimitive(_baseAsset);
uint256 quoteAssetUnit = getUnitForPrimitive(_quoteAsset);
// If rates are both in ETH or both in USDif (baseAssetRateAsset == quoteAssetRateAsset) {
return (
__calcConversionAmountSameRateAsset(
_baseAssetAmount,
baseAssetUnit,
_baseAssetRate,
quoteAssetUnit,
_quoteAssetRate
),
true
);
}
int256 ethPerUsdRate = IChainlinkAggregator(ethUsdAggregator).latestAnswer();
if (ethPerUsdRate <=0) {
return (0, false);
}
// If _baseAsset's rate is in ETH and _quoteAsset's rate is in USDif (baseAssetRateAsset == RateAsset.ETH) {
return (
__calcConversionAmountEthRateAssetToUsdRateAsset(
_baseAssetAmount,
baseAssetUnit,
_baseAssetRate,
quoteAssetUnit,
_quoteAssetRate,
uint256(ethPerUsdRate)
),
true
);
}
// If _baseAsset's rate is in USD and _quoteAsset's rate is in ETHreturn (
__calcConversionAmountUsdRateAssetToEthRateAsset(
_baseAssetAmount,
baseAssetUnit,
_baseAssetRate,
quoteAssetUnit,
_quoteAssetRate,
uint256(ethPerUsdRate)
),
true
);
}
/// @dev Helper to convert amounts where the base asset has an ETH rate and the quote asset has a USD ratefunction__calcConversionAmountEthRateAssetToUsdRateAsset(uint256 _baseAssetAmount,
uint256 _baseAssetUnit,
uint256 _baseAssetRate,
uint256 _quoteAssetUnit,
uint256 _quoteAssetRate,
uint256 _ethPerUsdRate
) privatepurereturns (uint256 quoteAssetAmount_) {
// Only allows two consecutive multiplication operations to avoid potential overflow.// Intermediate step needed to resolve stack-too-deep error.uint256 intermediateStep = _baseAssetAmount.mul(_baseAssetRate).mul(_ethPerUsdRate).div(
ETH_UNIT
);
return intermediateStep.mul(_quoteAssetUnit).div(_baseAssetUnit).div(_quoteAssetRate);
}
/// @dev Helper to convert amounts where base and quote assets both have ETH rates or both have USD ratesfunction__calcConversionAmountSameRateAsset(uint256 _baseAssetAmount,
uint256 _baseAssetUnit,
uint256 _baseAssetRate,
uint256 _quoteAssetUnit,
uint256 _quoteAssetRate
) privatepurereturns (uint256 quoteAssetAmount_) {
// Only allows two consecutive multiplication operations to avoid potential overflowreturn
_baseAssetAmount.mul(_baseAssetRate).mul(_quoteAssetUnit).div(
_baseAssetUnit.mul(_quoteAssetRate)
);
}
/// @dev Helper to convert amounts where the base asset has a USD rate and the quote asset has an ETH ratefunction__calcConversionAmountUsdRateAssetToEthRateAsset(uint256 _baseAssetAmount,
uint256 _baseAssetUnit,
uint256 _baseAssetRate,
uint256 _quoteAssetUnit,
uint256 _quoteAssetRate,
uint256 _ethPerUsdRate
) privatepurereturns (uint256 quoteAssetAmount_) {
// Only allows two consecutive multiplication operations to avoid potential overflow// Intermediate step needed to resolve stack-too-deep error.uint256 intermediateStep = _baseAssetAmount.mul(_baseAssetRate).mul(_quoteAssetUnit).div(
_ethPerUsdRate
);
return intermediateStep.mul(ETH_UNIT).div(_baseAssetUnit).div(_quoteAssetRate);
}
/// @dev Helper to get the latest rate for a given primitivefunction__getLatestRateData(address _primitive) privateviewreturns (int256 rate_) {
if (_primitive == WETH_TOKEN) {
returnint256(ETH_UNIT);
}
address aggregator = primitiveToAggregatorInfo[_primitive].aggregator;
require(aggregator !=address(0), "__getLatestRateData: Primitive does not exist");
return IChainlinkAggregator(aggregator).latestAnswer();
}
/// @dev Helper to set the `ethUsdAggregator` valuefunction__setEthUsdAggregator(address _nextEthUsdAggregator) private{
address prevEthUsdAggregator = ethUsdAggregator;
require(
_nextEthUsdAggregator != prevEthUsdAggregator,
"__setEthUsdAggregator: Value already set"
);
__validateAggregator(_nextEthUsdAggregator);
ethUsdAggregator = _nextEthUsdAggregator;
emit EthUsdAggregatorSet(prevEthUsdAggregator, _nextEthUsdAggregator);
}
/////////////////////////// PRIMITIVES REGISTRY ////////////////////////////// @notice Adds a list of primitives with the given aggregator and rateAsset values/// @param _primitives The primitives to add/// @param _aggregators The ordered aggregators corresponding to the list of _primitives/// @param _rateAssets The ordered rate assets corresponding to the list of _primitivesfunctionaddPrimitives(address[] calldata _primitives,
address[] calldata _aggregators,
RateAsset[] calldata _rateAssets
) externalonlyDispatcherOwner{
require(_primitives.length>0, "addPrimitives: _primitives cannot be empty");
__addPrimitives(_primitives, _aggregators, _rateAssets);
}
/// @notice Removes a list of primitives from the feed/// @param _primitives The primitives to removefunctionremovePrimitives(address[] calldata _primitives) externalonlyDispatcherOwner{
require(_primitives.length>0, "removePrimitives: _primitives cannot be empty");
for (uint256 i; i < _primitives.length; i++) {
require(
primitiveToAggregatorInfo[_primitives[i]].aggregator !=address(0),
"removePrimitives: Primitive not yet added"
);
delete primitiveToAggregatorInfo[_primitives[i]];
delete primitiveToUnit[_primitives[i]];
emit PrimitiveRemoved(_primitives[i]);
}
}
/// @notice Removes stale primitives from the feed/// @param _primitives The stale primitives to remove/// @dev Callable by anybodyfunctionremoveStalePrimitives(address[] calldata _primitives) external{
require(_primitives.length>0, "removeStalePrimitives: _primitives cannot be empty");
for (uint256 i; i < _primitives.length; i++) {
address aggregatorAddress = primitiveToAggregatorInfo[_primitives[i]].aggregator;
require(aggregatorAddress !=address(0), "removeStalePrimitives: Invalid primitive");
require(rateIsStale(aggregatorAddress), "removeStalePrimitives: Rate is not stale");
delete primitiveToAggregatorInfo[_primitives[i]];
delete primitiveToUnit[_primitives[i]];
emit StalePrimitiveRemoved(_primitives[i]);
}
}
/// @notice Sets the `staleRateThreshold` variable/// @param _nextStaleRateThreshold The next `staleRateThreshold` valuefunctionsetStaleRateThreshold(uint256 _nextStaleRateThreshold) externalonlyDispatcherOwner{
uint256 prevStaleRateThreshold = staleRateThreshold;
require(
_nextStaleRateThreshold != prevStaleRateThreshold,
"__setStaleRateThreshold: Value already set"
);
staleRateThreshold = _nextStaleRateThreshold;
emit StaleRateThresholdSet(prevStaleRateThreshold, _nextStaleRateThreshold);
}
/// @notice Updates the aggregators for given primitives/// @param _primitives The primitives to update/// @param _aggregators The ordered aggregators corresponding to the list of _primitivesfunctionupdatePrimitives(address[] calldata _primitives, address[] calldata _aggregators)
externalonlyDispatcherOwner{
require(_primitives.length>0, "updatePrimitives: _primitives cannot be empty");
require(
_primitives.length== _aggregators.length,
"updatePrimitives: Unequal _primitives and _aggregators array lengths"
);
for (uint256 i; i < _primitives.length; i++) {
address prevAggregator = primitiveToAggregatorInfo[_primitives[i]].aggregator;
require(prevAggregator !=address(0), "updatePrimitives: Primitive not yet added");
require(_aggregators[i] != prevAggregator, "updatePrimitives: Value already set");
__validateAggregator(_aggregators[i]);
primitiveToAggregatorInfo[_primitives[i]].aggregator = _aggregators[i];
emit PrimitiveUpdated(_primitives[i], prevAggregator, _aggregators[i]);
}
}
/// @notice Checks whether the current rate is considered stale for the specified aggregator/// @param _aggregator The Chainlink aggregator of which to check staleness/// @return rateIsStale_ True if the rate is considered stalefunctionrateIsStale(address _aggregator) publicviewreturns (bool rateIsStale_) {
return
IChainlinkAggregator(_aggregator).latestTimestamp() <block.timestamp.sub(staleRateThreshold);
}
/// @dev Helper to add primitives to the feedfunction__addPrimitives(address[] memory _primitives,
address[] memory _aggregators,
RateAsset[] memory _rateAssets
) private{
require(
_primitives.length== _aggregators.length,
"__addPrimitives: Unequal _primitives and _aggregators array lengths"
);
require(
_primitives.length== _rateAssets.length,
"__addPrimitives: Unequal _primitives and _rateAssets array lengths"
);
for (uint256 i =0; i < _primitives.length; i++) {
require(
primitiveToAggregatorInfo[_primitives[i]].aggregator ==address(0),
"__addPrimitives: Value already set"
);
__validateAggregator(_aggregators[i]);
primitiveToAggregatorInfo[_primitives[i]] = AggregatorInfo({
aggregator: _aggregators[i],
rateAsset: _rateAssets[i]
});
// Store the amount that makes up 1 unit given the asset's decimalsuint256 unit =10**uint256(ERC20(_primitives[i]).decimals());
primitiveToUnit[_primitives[i]] = unit;
emit PrimitiveAdded(_primitives[i], _aggregators[i], _rateAssets[i], unit);
}
}
/// @dev Helper to validate an aggregator by checking its return values for the expected interfacefunction__validateAggregator(address _aggregator) privateview{
require(_aggregator !=address(0), "__validateAggregator: Empty _aggregator");
require(
IChainlinkAggregator(_aggregator).latestAnswer() >0,
"__validateAggregator: No rate detected"
);
require(!rateIsStale(_aggregator), "__validateAggregator: Stale rate detected");
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the aggregatorInfo variable value for a primitive/// @param _primitive The primitive asset for which to get the aggregatorInfo value/// @return aggregatorInfo_ The aggregatorInfo valuefunctiongetAggregatorInfoForPrimitive(address _primitive)
externalviewreturns (AggregatorInfo memory aggregatorInfo_)
{
return primitiveToAggregatorInfo[_primitive];
}
/// @notice Gets the `ethUsdAggregator` variable value/// @return ethUsdAggregator_ The `ethUsdAggregator` variable valuefunctiongetEthUsdAggregator() externalviewreturns (address ethUsdAggregator_) {
return ethUsdAggregator;
}
/// @notice Gets the `staleRateThreshold` variable value/// @return staleRateThreshold_ The `staleRateThreshold` variable valuefunctiongetStaleRateThreshold() externalviewreturns (uint256 staleRateThreshold_) {
return staleRateThreshold;
}
/// @notice Gets the `WETH_TOKEN` variable value/// @return wethToken_ The `WETH_TOKEN` variable valuefunctiongetWethToken() externalviewreturns (address wethToken_) {
return WETH_TOKEN;
}
/// @notice Gets the rateAsset variable value for a primitive/// @return rateAsset_ The rateAsset variable value/// @dev This isn't strictly necessary as WETH_TOKEN will be undefined and thus/// the RateAsset will be the 0-position of the enum (i.e. ETH), but it makes the/// behavior more explicitfunctiongetRateAssetForPrimitive(address _primitive)
publicviewreturns (RateAsset rateAsset_)
{
if (_primitive == WETH_TOKEN) {
return RateAsset.ETH;
}
return primitiveToAggregatorInfo[_primitive].rateAsset;
}
/// @notice Gets the unit variable value for a primitive/// @return unit_ The unit variable valuefunctiongetUnitForPrimitive(address _primitive) publicviewreturns (uint256 unit_) {
if (_primitive == WETH_TOKEN) {
return ETH_UNIT;
}
return primitiveToUnit[_primitive];
}
}
Contract Source Code
File 20 of 138: CompoundAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../infrastructure/price-feeds/derivatives/feeds/CompoundPriceFeed.sol";
import"../../../../interfaces/ICERC20.sol";
import"../../../../interfaces/ICEther.sol";
import"../../../../interfaces/IWETH.sol";
import"../utils/AdapterBase.sol";
/// @title CompoundAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for Compound <https://compound.finance/>contractCompoundAdapterisAdapterBase{
addressprivateimmutable COMPOUND_PRICE_FEED;
addressprivateimmutable WETH_TOKEN;
constructor(address _integrationManager,
address _compoundPriceFeed,
address _wethToken
) publicAdapterBase(_integrationManager) {
COMPOUND_PRICE_FEED = _compoundPriceFeed;
WETH_TOKEN = _wethToken;
}
/// @dev Needed to receive ETH during cEther lend/redeemreceive() externalpayable{}
/// @notice Provides a constant string identifier for an adapter/// @return identifier_ An identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"COMPOUND";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
if (_selector == LEND_SELECTOR) {
(address cToken, uint256 tokenAmount, uint256 minCTokenAmount) = __decodeCallArgs(
_encodedCallArgs
);
address token = CompoundPriceFeed(COMPOUND_PRICE_FEED).getTokenFromCToken(cToken);
require(token !=address(0), "parseAssetsForMethod: Unsupported cToken");
spendAssets_ =newaddress[](1);
spendAssets_[0] = token;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = tokenAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = cToken;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minCTokenAmount;
} elseif (_selector == REDEEM_SELECTOR) {
(address cToken, uint256 cTokenAmount, uint256 minTokenAmount) = __decodeCallArgs(
_encodedCallArgs
);
address token = CompoundPriceFeed(COMPOUND_PRICE_FEED).getTokenFromCToken(cToken);
require(token !=address(0), "parseAssetsForMethod: Unsupported cToken");
spendAssets_ =newaddress[](1);
spendAssets_[0] = cToken;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = cTokenAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = token;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minTokenAmount;
} else {
revert("parseAssetsForMethod: _selector invalid");
}
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Lends an amount of a token to Compound/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionlend(address _vaultProxy,
bytescalldata,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
// More efficient to parse all from _encodedAssetTransferArgs
(
,
address[] memory spendAssets,
uint256[] memory spendAssetAmounts,
address[] memory incomingAssets
) = __decodeEncodedAssetTransferArgs(_encodedAssetTransferArgs);
if (spendAssets[0] == WETH_TOKEN) {
IWETH(WETH_TOKEN).withdraw(spendAssetAmounts[0]);
ICEther(incomingAssets[0]).mint{value: spendAssetAmounts[0]}();
} else {
__approveMaxAsNeeded(spendAssets[0], incomingAssets[0], spendAssetAmounts[0]);
ICERC20(incomingAssets[0]).mint(spendAssetAmounts[0]);
}
}
/// @notice Redeems an amount of cTokens from Compound/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionredeem(address _vaultProxy,
bytescalldata,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
// More efficient to parse all from _encodedAssetTransferArgs
(
,
address[] memory spendAssets,
uint256[] memory spendAssetAmounts,
address[] memory incomingAssets
) = __decodeEncodedAssetTransferArgs(_encodedAssetTransferArgs);
ICERC20(spendAssets[0]).redeem(spendAssetAmounts[0]);
if (incomingAssets[0] == WETH_TOKEN) {
IWETH(payable(WETH_TOKEN)).deposit{value: payable(address(this)).balance}();
}
}
// PRIVATE FUNCTIONS/// @dev Helper to decode callArgs for lend and redeemfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address cToken_,
uint256 outgoingAssetAmount_,
uint256 minIncomingAssetAmount_
)
{
returnabi.decode(_encodedCallArgs, (address, uint256, uint256));
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `COMPOUND_PRICE_FEED` variable/// @return compoundPriceFeed_ The `COMPOUND_PRICE_FEED` variable valuefunctiongetCompoundPriceFeed() externalviewreturns (address compoundPriceFeed_) {
return COMPOUND_PRICE_FEED;
}
/// @notice Gets the `WETH_TOKEN` variable/// @return wethToken_ The `WETH_TOKEN` variable valuefunctiongetWethToken() externalviewreturns (address wethToken_) {
return WETH_TOKEN;
}
}
Contract Source Code
File 21 of 138: CompoundPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/ICERC20.sol";
import"../../../utils/DispatcherOwnerMixin.sol";
import"../IDerivativePriceFeed.sol";
/// @title CompoundPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Price source oracle for Compound Tokens (cTokens)contractCompoundPriceFeedisIDerivativePriceFeed, DispatcherOwnerMixin{
usingSafeMathforuint256;
eventCTokenAdded(addressindexed cToken, addressindexed token);
uint256privateconstant CTOKEN_RATE_DIVISOR =10**18;
mapping(address=>address) private cTokenToToken;
constructor(address _dispatcher,
address _weth,
address _ceth,
address[] memory cERC20Tokens
) publicDispatcherOwnerMixin(_dispatcher) {
// Set cEth
cTokenToToken[_ceth] = _weth;
emit CTokenAdded(_ceth, _weth);
// Set any other cTokensif (cERC20Tokens.length>0) {
__addCERC20Tokens(cERC20Tokens);
}
}
/// @notice Converts a given amount of a derivative to its underlying asset values/// @param _derivative The derivative to convert/// @param _derivativeAmount The amount of the derivative to convert/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amountfunctioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
underlyings_ =newaddress[](1);
underlyings_[0] = cTokenToToken[_derivative];
require(underlyings_[0] !=address(0), "calcUnderlyingValues: Unsupported derivative");
underlyingAmounts_ =newuint256[](1);
// Returns a rate scaled to 10^18
underlyingAmounts_[0] = _derivativeAmount
.mul(ICERC20(_derivative).exchangeRateStored())
.div(CTOKEN_RATE_DIVISOR);
return (underlyings_, underlyingAmounts_);
}
/// @notice Checks if an asset is supported by the price feed/// @param _asset The asset to check/// @return isSupported_ True if the asset is supportedfunctionisSupportedAsset(address _asset) externalviewoverridereturns (bool isSupported_) {
return cTokenToToken[_asset] !=address(0);
}
//////////////////////// CTOKENS REGISTRY /////////////////////////// @notice Adds cTokens to the price feed/// @param _cTokens cTokens to add/// @dev Only allows CERC20 tokens. CEther is set in the constructor.functionaddCTokens(address[] calldata _cTokens) externalonlyDispatcherOwner{
__addCERC20Tokens(_cTokens);
}
/// @dev Helper to add cTokensfunction__addCERC20Tokens(address[] memory _cTokens) private{
require(_cTokens.length>0, "__addCTokens: Empty _cTokens");
for (uint256 i; i < _cTokens.length; i++) {
require(cTokenToToken[_cTokens[i]] ==address(0), "__addCTokens: Value already set");
address token = ICERC20(_cTokens[i]).underlying();
cTokenToToken[_cTokens[i]] = token;
emit CTokenAdded(_cTokens[i], token);
}
}
////////////////////// STATE GETTERS //////////////////////// @notice Returns the underlying asset of a given cToken/// @param _cToken The cToken for which to get the underlying asset/// @return token_ The underlying tokenfunctiongetTokenFromCToken(address _cToken) publicviewreturns (address token_) {
return cTokenToToken[_cToken];
}
}
Contract Source Code
File 22 of 138: ComptrollerLib.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"../../../../persistent/dispatcher/IDispatcher.sol";
import"../../../extensions/IExtension.sol";
import"../../../extensions/fee-manager/IFeeManager.sol";
import"../../../extensions/policy-manager/IPolicyManager.sol";
import"../../../infrastructure/price-feeds/primitives/IPrimitivePriceFeed.sol";
import"../../../infrastructure/value-interpreter/IValueInterpreter.sol";
import"../../../utils/AddressArrayLib.sol";
import"../../../utils/AssetFinalityResolver.sol";
import"../../fund-deployer/IFundDeployer.sol";
import"../vault/IVault.sol";
import"./IComptroller.sol";
/// @title ComptrollerLib Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice The core logic library shared by all fundscontractComptrollerLibisIComptroller, AssetFinalityResolver{
usingAddressArrayLibforaddress[];
usingSafeMathforuint256;
usingSafeERC20forERC20;
eventMigratedSharesDuePaid(uint256 sharesDue);
eventOverridePauseSet(boolindexed overridePause);
eventPreRedeemSharesHookFailed(bytes failureReturnData,
address redeemer,
uint256 sharesQuantity
);
eventSharesBought(addressindexed caller,
addressindexed buyer,
uint256 investmentAmount,
uint256 sharesIssued,
uint256 sharesReceived
);
eventSharesRedeemed(addressindexed redeemer,
uint256 sharesQuantity,
address[] receivedAssets,
uint256[] receivedAssetQuantities
);
eventVaultProxySet(address vaultProxy);
// Constants and immutables - shared by all proxiesuint256privateconstant SHARES_UNIT =10**18;
addressprivateimmutable DISPATCHER;
addressprivateimmutable FUND_DEPLOYER;
addressprivateimmutable FEE_MANAGER;
addressprivateimmutable INTEGRATION_MANAGER;
addressprivateimmutable PRIMITIVE_PRICE_FEED;
addressprivateimmutable POLICY_MANAGER;
addressprivateimmutable VALUE_INTERPRETER;
// Pseudo-constants (can only be set once)addressinternal denominationAsset;
addressinternal vaultProxy;
// True only for the one non-proxyboolinternal isLib;
// Storage// Allows a fund owner to override a release-level pauseboolinternal overridePause;
// A reverse-mutex, granting atomic permission for particular contracts to make vault callsboolinternal permissionedVaultActionAllowed;
// A mutex to protect against reentrancyboolinternal reentranceLocked;
// A timelock between any "shares actions" (i.e., buy and redeem shares), per-accountuint256internal sharesActionTimelock;
mapping(address=>uint256) internal acctToLastSharesAction;
///////////////// MODIFIERS /////////////////modifierallowsPermissionedVaultAction{
__assertPermissionedVaultActionNotAllowed();
permissionedVaultActionAllowed =true;
_;
permissionedVaultActionAllowed =false;
}
modifierlocksReentrance() {
__assertNotReentranceLocked();
reentranceLocked =true;
_;
reentranceLocked =false;
}
modifieronlyActive() {
__assertIsActive(vaultProxy);
_;
}
modifieronlyNotPaused() {
__assertNotPaused();
_;
}
modifieronlyFundDeployer() {
__assertIsFundDeployer(msg.sender);
_;
}
modifieronlyOwner() {
__assertIsOwner(msg.sender);
_;
}
modifiertimelockedSharesAction(address _account) {
__assertSharesActionNotTimelocked(_account);
_;
acctToLastSharesAction[_account] =block.timestamp;
}
// ASSERTION HELPERS// Modifiers are inefficient in terms of contract size,// so we use helper functions to prevent repetitive inlining of expensive string values./// @dev Since vaultProxy is set during activate(),/// we can check that var rather than storing additional statefunction__assertIsActive(address _vaultProxy) privatepure{
require(_vaultProxy !=address(0), "Fund not active");
}
function__assertIsFundDeployer(address _who) privateview{
require(_who == FUND_DEPLOYER, "Only FundDeployer callable");
}
function__assertIsOwner(address _who) privateview{
require(_who == IVault(vaultProxy).getOwner(), "Only fund owner callable");
}
function__assertLowLevelCall(bool _success, bytesmemory _returnData) privatepure{
require(_success, string(_returnData));
}
function__assertNotPaused() privateview{
require(!__fundIsPaused(), "Fund is paused");
}
function__assertNotReentranceLocked() privateview{
require(!reentranceLocked, "Re-entrance");
}
function__assertPermissionedVaultActionNotAllowed() privateview{
require(!permissionedVaultActionAllowed, "Vault action re-entrance");
}
function__assertSharesActionNotTimelocked(address _account) privateview{
require(
block.timestamp.sub(acctToLastSharesAction[_account]) >= sharesActionTimelock,
"Shares action timelocked"
);
}
constructor(address _dispatcher,
address _fundDeployer,
address _valueInterpreter,
address _feeManager,
address _integrationManager,
address _policyManager,
address _primitivePriceFeed,
address _synthetixPriceFeed,
address _synthetixAddressResolver
) publicAssetFinalityResolver(_synthetixPriceFeed, _synthetixAddressResolver) {
DISPATCHER = _dispatcher;
FEE_MANAGER = _feeManager;
FUND_DEPLOYER = _fundDeployer;
INTEGRATION_MANAGER = _integrationManager;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
POLICY_MANAGER = _policyManager;
VALUE_INTERPRETER = _valueInterpreter;
isLib =true;
}
/////////////// GENERAL ////////////////// @notice Calls a specified action on an Extension/// @param _extension The Extension contract to call (e.g., FeeManager)/// @param _actionId An ID representing the action to take on the extension (see extension)/// @param _callArgs The encoded data for the call/// @dev Used to route arbitrary calls, so that msg.sender is the ComptrollerProxy/// (for access control). Uses a mutex of sorts that allows "permissioned vault actions"/// during calls originating from this function.functioncallOnExtension(address _extension,
uint256 _actionId,
bytescalldata _callArgs
) externaloverrideonlyNotPausedonlyActivelocksReentranceallowsPermissionedVaultAction{
require(
_extension == FEE_MANAGER || _extension == INTEGRATION_MANAGER,
"callOnExtension: _extension invalid"
);
IExtension(_extension).receiveCallFromComptroller(msg.sender, _actionId, _callArgs);
}
/// @notice Sets or unsets an override on a release-wide pause/// @param _nextOverridePause True if the pause should be overrodefunctionsetOverridePause(bool _nextOverridePause) externalonlyOwner{
require(_nextOverridePause != overridePause, "setOverridePause: Value already set");
overridePause = _nextOverridePause;
emit OverridePauseSet(_nextOverridePause);
}
/// @notice Makes an arbitrary call with the VaultProxy contract as the sender/// @param _contract The contract to call/// @param _selector The selector to call/// @param _encodedArgs The encoded arguments for the callfunctionvaultCallOnContract(address _contract,
bytes4 _selector,
bytescalldata _encodedArgs
) externalonlyNotPausedonlyActiveonlyOwner{
require(
IFundDeployer(FUND_DEPLOYER).isRegisteredVaultCall(_contract, _selector),
"vaultCallOnContract: Unregistered"
);
IVault(vaultProxy).callOnContract(_contract, abi.encodePacked(_selector, _encodedArgs));
}
/// @dev Helper to check whether the release is paused, and that there is no local overridefunction__fundIsPaused() privateviewreturns (bool) {
return
IFundDeployer(FUND_DEPLOYER).getReleaseStatus() ==
IFundDeployer.ReleaseStatus.Paused &&!overridePause;
}
////////////////////////////////// PERMISSIONED VAULT ACTIONS ///////////////////////////////////// @notice Makes a permissioned, state-changing call on the VaultProxy contract/// @param _action The enum representing the VaultAction to perform on the VaultProxy/// @param _actionData The call data for the action to performfunctionpermissionedVaultAction(VaultAction _action, bytescalldata _actionData)
externaloverrideonlyNotPausedonlyActive{
__assertPermissionedVaultAction(msg.sender, _action);
if (_action == VaultAction.AddTrackedAsset) {
__vaultActionAddTrackedAsset(_actionData);
} elseif (_action == VaultAction.ApproveAssetSpender) {
__vaultActionApproveAssetSpender(_actionData);
} elseif (_action == VaultAction.BurnShares) {
__vaultActionBurnShares(_actionData);
} elseif (_action == VaultAction.MintShares) {
__vaultActionMintShares(_actionData);
} elseif (_action == VaultAction.RemoveTrackedAsset) {
__vaultActionRemoveTrackedAsset(_actionData);
} elseif (_action == VaultAction.TransferShares) {
__vaultActionTransferShares(_actionData);
} elseif (_action == VaultAction.WithdrawAssetTo) {
__vaultActionWithdrawAssetTo(_actionData);
}
}
/// @dev Helper to assert that a caller is allowed to perform a particular VaultActionfunction__assertPermissionedVaultAction(address _caller, VaultAction _action) privateview{
require(
permissionedVaultActionAllowed,
"__assertPermissionedVaultAction: No action allowed"
);
if (_caller == INTEGRATION_MANAGER) {
require(
_action == VaultAction.ApproveAssetSpender ||
_action == VaultAction.AddTrackedAsset ||
_action == VaultAction.RemoveTrackedAsset ||
_action == VaultAction.WithdrawAssetTo,
"__assertPermissionedVaultAction: Not valid for IntegrationManager"
);
} elseif (_caller == FEE_MANAGER) {
require(
_action == VaultAction.BurnShares ||
_action == VaultAction.MintShares ||
_action == VaultAction.TransferShares,
"__assertPermissionedVaultAction: Not valid for FeeManager"
);
} else {
revert("__assertPermissionedVaultAction: Not a valid actor");
}
}
/// @dev Helper to add a tracked asset to the fundfunction__vaultActionAddTrackedAsset(bytesmemory _actionData) private{
address asset =abi.decode(_actionData, (address));
IVault(vaultProxy).addTrackedAsset(asset);
}
/// @dev Helper to grant a spender an allowance for a fund's assetfunction__vaultActionApproveAssetSpender(bytesmemory _actionData) private{
(address asset, address target, uint256 amount) =abi.decode(
_actionData,
(address, address, uint256)
);
IVault(vaultProxy).approveAssetSpender(asset, target, amount);
}
/// @dev Helper to burn fund shares for a particular accountfunction__vaultActionBurnShares(bytesmemory _actionData) private{
(address target, uint256 amount) =abi.decode(_actionData, (address, uint256));
IVault(vaultProxy).burnShares(target, amount);
}
/// @dev Helper to mint fund shares to a particular accountfunction__vaultActionMintShares(bytesmemory _actionData) private{
(address target, uint256 amount) =abi.decode(_actionData, (address, uint256));
IVault(vaultProxy).mintShares(target, amount);
}
/// @dev Helper to remove a tracked asset from the fundfunction__vaultActionRemoveTrackedAsset(bytesmemory _actionData) private{
address asset =abi.decode(_actionData, (address));
// Allowing this to fail silently makes it cheaper and simpler// for Extensions to not query for the denomination assetif (asset != denominationAsset) {
IVault(vaultProxy).removeTrackedAsset(asset);
}
}
/// @dev Helper to transfer fund shares from one account to anotherfunction__vaultActionTransferShares(bytesmemory _actionData) private{
(addressfrom, address to, uint256 amount) =abi.decode(
_actionData,
(address, address, uint256)
);
IVault(vaultProxy).transferShares(from, to, amount);
}
/// @dev Helper to withdraw an asset from the VaultProxy to a given accountfunction__vaultActionWithdrawAssetTo(bytesmemory _actionData) private{
(address asset, address target, uint256 amount) =abi.decode(
_actionData,
(address, address, uint256)
);
IVault(vaultProxy).withdrawAssetTo(asset, target, amount);
}
///////////////// LIFECYCLE //////////////////// @notice Initializes a fund with its core config/// @param _denominationAsset The asset in which the fund's value should be denominated/// @param _sharesActionTimelock The minimum number of seconds between any two "shares actions"/// (buying or selling shares) by the same user/// @dev Pseudo-constructor per proxy./// No need to assert access because this is called atomically on deployment,/// and once it's called, it cannot be called again.functioninit(address _denominationAsset, uint256 _sharesActionTimelock) externaloverride{
require(denominationAsset ==address(0), "init: Already initialized");
require(
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_denominationAsset),
"init: Bad denomination asset"
);
denominationAsset = _denominationAsset;
sharesActionTimelock = _sharesActionTimelock;
}
/// @notice Configure the extensions of a fund/// @param _feeManagerConfigData Encoded config for fees to enable/// @param _policyManagerConfigData Encoded config for policies to enable/// @dev No need to assert anything beyond FundDeployer access./// Called atomically with init(), but after ComptrollerLib has been deployed,/// giving access to its state and interfacefunctionconfigureExtensions(bytescalldata _feeManagerConfigData,
bytescalldata _policyManagerConfigData
) externaloverrideonlyFundDeployer{
if (_feeManagerConfigData.length>0) {
IExtension(FEE_MANAGER).setConfigForFund(_feeManagerConfigData);
}
if (_policyManagerConfigData.length>0) {
IExtension(POLICY_MANAGER).setConfigForFund(_policyManagerConfigData);
}
}
/// @notice Activates the fund by attaching a VaultProxy and activating all Extensions/// @param _vaultProxy The VaultProxy to attach to the fund/// @param _isMigration True if a migrated fund is being activated/// @dev No need to assert anything beyond FundDeployer access.functionactivate(address _vaultProxy, bool _isMigration) externaloverrideonlyFundDeployer{
vaultProxy = _vaultProxy;
emit VaultProxySet(_vaultProxy);
if (_isMigration) {
// Distribute any shares in the VaultProxy to the fund owner.// This is a mechanism to ensure that even in the edge case of a fund being unable// to payout fee shares owed during migration, these shares are not lost.uint256 sharesDue = ERC20(_vaultProxy).balanceOf(_vaultProxy);
if (sharesDue >0) {
IVault(_vaultProxy).transferShares(
_vaultProxy,
IVault(_vaultProxy).getOwner(),
sharesDue
);
emit MigratedSharesDuePaid(sharesDue);
}
}
// Note: a future release could consider forcing the adding of a tracked asset here,// just in case a fund is migrating from an old configuration where they are not able// to remove an asset to get under the tracked assets limit
IVault(_vaultProxy).addTrackedAsset(denominationAsset);
// Activate extensions
IExtension(FEE_MANAGER).activateForFund(_isMigration);
IExtension(INTEGRATION_MANAGER).activateForFund(_isMigration);
IExtension(POLICY_MANAGER).activateForFund(_isMigration);
}
/// @notice Remove the config for a fund/// @dev No need to assert anything beyond FundDeployer access./// Calling onlyNotPaused here rather than in the FundDeployer allows/// the owner to potentially override the pause and rescue unpaid fees.functiondestruct()
externaloverrideonlyFundDeployeronlyNotPausedallowsPermissionedVaultAction{
// Failsafe to protect the libs against selfdestructrequire(!isLib, "destruct: Only delegate callable");
// Deactivate the extensions
IExtension(FEE_MANAGER).deactivateForFund();
IExtension(INTEGRATION_MANAGER).deactivateForFund();
IExtension(POLICY_MANAGER).deactivateForFund();
// Delete storage of ComptrollerProxy// There should never be ETH in the ComptrollerLib, so no need to waste gas// to get the fund ownerselfdestruct(address(0));
}
////////////////// ACCOUNTING ///////////////////// @notice Calculates the gross asset value (GAV) of the fund/// @param _requireFinality True if all assets must have exact final balances settled/// @return gav_ The fund GAV/// @return isValid_ True if the conversion rates used to derive the GAV are all validfunctioncalcGav(bool _requireFinality) publicoverridereturns (uint256 gav_, bool isValid_) {
address vaultProxyAddress = vaultProxy;
address[] memory assets = IVault(vaultProxyAddress).getTrackedAssets();
if (assets.length==0) {
return (0, true);
}
uint256[] memory balances =newuint256[](assets.length);
for (uint256 i; i < assets.length; i++) {
balances[i] = __finalizeIfSynthAndGetAssetBalance(
vaultProxyAddress,
assets[i],
_requireFinality
);
}
(gav_, isValid_) = IValueInterpreter(VALUE_INTERPRETER).calcCanonicalAssetsTotalValue(
assets,
balances,
denominationAsset
);
return (gav_, isValid_);
}
/// @notice Calculates the gross value of 1 unit of shares in the fund's denomination asset/// @param _requireFinality True if all assets must have exact final balances settled/// @return grossShareValue_ The amount of the denomination asset per share/// @return isValid_ True if the conversion rates to derive the value are all valid/// @dev Does not account for any fees outstanding.functioncalcGrossShareValue(bool _requireFinality)
externaloverridereturns (uint256 grossShareValue_, bool isValid_)
{
uint256 gav;
(gav, isValid_) = calcGav(_requireFinality);
grossShareValue_ = __calcGrossShareValue(
gav,
ERC20(vaultProxy).totalSupply(),
10**uint256(ERC20(denominationAsset).decimals())
);
return (grossShareValue_, isValid_);
}
/// @dev Helper for calculating the gross share valuefunction__calcGrossShareValue(uint256 _gav,
uint256 _sharesSupply,
uint256 _denominationAssetUnit
) privatepurereturns (uint256 grossShareValue_) {
if (_sharesSupply ==0) {
return _denominationAssetUnit;
}
return _gav.mul(SHARES_UNIT).div(_sharesSupply);
}
///////////////////// PARTICIPATION /////////////////////// BUY SHARES/// @notice Buys shares in the fund for multiple sets of criteria/// @param _buyers The accounts for which to buy shares/// @param _investmentAmounts The amounts of the fund's denomination asset/// with which to buy shares for the corresponding _buyers/// @param _minSharesQuantities The minimum quantities of shares to buy/// with the corresponding _investmentAmounts/// @return sharesReceivedAmounts_ The actual amounts of shares received/// by the corresponding _buyers/// @dev Param arrays have indexes corresponding to individual __buyShares() orders.functionbuyShares(address[] calldata _buyers,
uint256[] calldata _investmentAmounts,
uint256[] calldata _minSharesQuantities
)
externalonlyNotPausedlocksReentranceallowsPermissionedVaultActionreturns (uint256[] memory sharesReceivedAmounts_)
{
require(_buyers.length>0, "buyShares: Empty _buyers");
require(
_buyers.length== _investmentAmounts.length&&
_buyers.length== _minSharesQuantities.length,
"buyShares: Unequal arrays"
);
address vaultProxyCopy = vaultProxy;
__assertIsActive(vaultProxyCopy);
require(
!IDispatcher(DISPATCHER).hasMigrationRequest(vaultProxyCopy),
"buyShares: Pending migration"
);
(uint256 gav, bool gavIsValid) = calcGav(true);
require(gavIsValid, "buyShares: Invalid GAV");
__buySharesSetupHook(msg.sender, _investmentAmounts, gav);
address denominationAssetCopy = denominationAsset;
uint256 sharePrice = __calcGrossShareValue(
gav,
ERC20(vaultProxyCopy).totalSupply(),
10**uint256(ERC20(denominationAssetCopy).decimals())
);
sharesReceivedAmounts_ =newuint256[](_buyers.length);
for (uint256 i; i < _buyers.length; i++) {
sharesReceivedAmounts_[i] = __buyShares(
_buyers[i],
_investmentAmounts[i],
_minSharesQuantities[i],
vaultProxyCopy,
sharePrice,
gav,
denominationAssetCopy
);
gav = gav.add(_investmentAmounts[i]);
}
__buySharesCompletedHook(msg.sender, sharesReceivedAmounts_, gav);
return sharesReceivedAmounts_;
}
/// @dev Helper to buy sharesfunction__buyShares(address _buyer,
uint256 _investmentAmount,
uint256 _minSharesQuantity,
address _vaultProxy,
uint256 _sharePrice,
uint256 _preBuySharesGav,
address _denominationAsset
) privatetimelockedSharesAction(_buyer) returns (uint256 sharesReceived_) {
require(_investmentAmount >0, "__buyShares: Empty _investmentAmount");
// Gives Extensions a chance to run logic prior to the minting of bought shares
__preBuySharesHook(_buyer, _investmentAmount, _minSharesQuantity, _preBuySharesGav);
// Calculate the amount of shares to issue with the investment amountuint256 sharesIssued = _investmentAmount.mul(SHARES_UNIT).div(_sharePrice);
// Mint shares to the buyeruint256 prevBuyerShares = ERC20(_vaultProxy).balanceOf(_buyer);
IVault(_vaultProxy).mintShares(_buyer, sharesIssued);
// Transfer the investment asset to the fund.// Does not follow the checks-effects-interactions pattern, but it is preferred// to have the final state of the VaultProxy prior to running __postBuySharesHook().
ERC20(_denominationAsset).safeTransferFrom(msg.sender, _vaultProxy, _investmentAmount);
// Gives Extensions a chance to run logic after shares are issued
__postBuySharesHook(_buyer, _investmentAmount, sharesIssued, _preBuySharesGav);
// The number of actual shares received may differ from shares issued due to// how the PostBuyShares hooks are invoked by Extensions (i.e., fees)
sharesReceived_ = ERC20(_vaultProxy).balanceOf(_buyer).sub(prevBuyerShares);
require(
sharesReceived_ >= _minSharesQuantity,
"__buyShares: Shares received < _minSharesQuantity"
);
emit SharesBought(msg.sender, _buyer, _investmentAmount, sharesIssued, sharesReceived_);
return sharesReceived_;
}
/// @dev Helper for Extension actions after all __buyShares() calls are madefunction__buySharesCompletedHook(address _caller,
uint256[] memory _sharesReceivedAmounts,
uint256 _gav
) private{
IPolicyManager(POLICY_MANAGER).validatePolicies(
address(this),
IPolicyManager.PolicyHook.BuySharesCompleted,
abi.encode(_caller, _sharesReceivedAmounts, _gav)
);
IFeeManager(FEE_MANAGER).invokeHook(
IFeeManager.FeeHook.BuySharesCompleted,
abi.encode(_caller, _sharesReceivedAmounts),
_gav
);
}
/// @dev Helper for Extension actions before any __buyShares() calls are madefunction__buySharesSetupHook(address _caller,
uint256[] memory _investmentAmounts,
uint256 _gav
) private{
IPolicyManager(POLICY_MANAGER).validatePolicies(
address(this),
IPolicyManager.PolicyHook.BuySharesSetup,
abi.encode(_caller, _investmentAmounts, _gav)
);
IFeeManager(FEE_MANAGER).invokeHook(
IFeeManager.FeeHook.BuySharesSetup,
abi.encode(_caller, _investmentAmounts),
_gav
);
}
/// @dev Helper for Extension actions immediately prior to issuing shares./// This could be cleaned up so both Extensions take the same encoded args and handle GAV/// in the same way, but there is not the obvious need for gas savings of recycling/// the GAV value for the current policies as there is for the fees.function__preBuySharesHook(address _buyer,
uint256 _investmentAmount,
uint256 _minSharesQuantity,
uint256 _gav
) private{
IFeeManager(FEE_MANAGER).invokeHook(
IFeeManager.FeeHook.PreBuyShares,
abi.encode(_buyer, _investmentAmount, _minSharesQuantity),
_gav
);
IPolicyManager(POLICY_MANAGER).validatePolicies(
address(this),
IPolicyManager.PolicyHook.PreBuyShares,
abi.encode(_buyer, _investmentAmount, _minSharesQuantity, _gav)
);
}
/// @dev Helper for Extension actions immediately after issuing shares./// Same comment applies from __preBuySharesHook() above.function__postBuySharesHook(address _buyer,
uint256 _investmentAmount,
uint256 _sharesIssued,
uint256 _preBuySharesGav
) private{
uint256 gav = _preBuySharesGav.add(_investmentAmount);
IFeeManager(FEE_MANAGER).invokeHook(
IFeeManager.FeeHook.PostBuyShares,
abi.encode(_buyer, _investmentAmount, _sharesIssued),
gav
);
IPolicyManager(POLICY_MANAGER).validatePolicies(
address(this),
IPolicyManager.PolicyHook.PostBuyShares,
abi.encode(_buyer, _investmentAmount, _sharesIssued, gav)
);
}
// REDEEM SHARES/// @notice Redeem all of the sender's shares for a proportionate slice of the fund's assets/// @return payoutAssets_ The assets paid out to the redeemer/// @return payoutAmounts_ The amount of each asset paid out to the redeemer/// @dev See __redeemShares() for further detailfunctionredeemShares()
externalreturns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_)
{
return
__redeemShares(
msg.sender,
ERC20(vaultProxy).balanceOf(msg.sender),
newaddress[](0),
newaddress[](0)
);
}
/// @notice Redeem a specified quantity of the sender's shares for a proportionate slice of/// the fund's assets, optionally specifying additional assets and assets to skip./// @param _sharesQuantity The quantity of shares to redeem/// @param _additionalAssets Additional (non-tracked) assets to claim/// @param _assetsToSkip Tracked assets to forfeit/// @return payoutAssets_ The assets paid out to the redeemer/// @return payoutAmounts_ The amount of each asset paid out to the redeemer/// @dev Any claim to passed _assetsToSkip will be forfeited entirely. This should generally/// only be exercised if a bad asset is causing redemption to fail.functionredeemSharesDetailed(uint256 _sharesQuantity,
address[] calldata _additionalAssets,
address[] calldata _assetsToSkip
) externalreturns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_) {
return __redeemShares(msg.sender, _sharesQuantity, _additionalAssets, _assetsToSkip);
}
/// @dev Helper to parse an array of payout assets during redemption, taking into account/// additional assets and assets to skip. _assetsToSkip ignores _additionalAssets./// All input arrays are assumed to be unique.function__parseRedemptionPayoutAssets(address[] memory _trackedAssets,
address[] memory _additionalAssets,
address[] memory _assetsToSkip
) privatepurereturns (address[] memory payoutAssets_) {
address[] memory trackedAssetsToPayout = _trackedAssets.removeItems(_assetsToSkip);
if (_additionalAssets.length==0) {
return trackedAssetsToPayout;
}
// Add additional assets. Duplicates of trackedAssets are ignored.bool[] memory indexesToAdd =newbool[](_additionalAssets.length);
uint256 additionalItemsCount;
for (uint256 i; i < _additionalAssets.length; i++) {
if (!trackedAssetsToPayout.contains(_additionalAssets[i])) {
indexesToAdd[i] =true;
additionalItemsCount++;
}
}
if (additionalItemsCount ==0) {
return trackedAssetsToPayout;
}
payoutAssets_ =newaddress[](trackedAssetsToPayout.length.add(additionalItemsCount));
for (uint256 i; i < trackedAssetsToPayout.length; i++) {
payoutAssets_[i] = trackedAssetsToPayout[i];
}
uint256 payoutAssetsIndex = trackedAssetsToPayout.length;
for (uint256 i; i < _additionalAssets.length; i++) {
if (indexesToAdd[i]) {
payoutAssets_[payoutAssetsIndex] = _additionalAssets[i];
payoutAssetsIndex++;
}
}
return payoutAssets_;
}
/// @dev Helper for system actions immediately prior to redeeming shares./// Policy validation is not currently allowed on redemption, to ensure continuous redeemability.function__preRedeemSharesHook(address _redeemer, uint256 _sharesQuantity)
privateallowsPermissionedVaultAction{
try
IFeeManager(FEE_MANAGER).invokeHook(
IFeeManager.FeeHook.PreRedeemShares,
abi.encode(_redeemer, _sharesQuantity),
0
)
{} catch (bytesmemory reason) {
emit PreRedeemSharesHookFailed(reason, _redeemer, _sharesQuantity);
}
}
/// @dev Helper to redeem shares./// This function should never fail without a way to bypass the failure, which is assured/// through two mechanisms:/// 1. The FeeManager is called with the try/catch pattern to assure that calls to it/// can never block redemption./// 2. If a token fails upon transfer(), that token can be skipped (and its balance forfeited)/// by explicitly specifying _assetsToSkip./// Because of these assurances, shares should always be redeemable, with the exception/// of the timelock period on shares actions that must be respected.function__redeemShares(address _redeemer,
uint256 _sharesQuantity,
address[] memory _additionalAssets,
address[] memory _assetsToSkip
)
privatelocksReentrancereturns (address[] memory payoutAssets_, uint256[] memory payoutAmounts_)
{
require(_sharesQuantity >0, "__redeemShares: _sharesQuantity must be >0");
require(
_additionalAssets.isUniqueSet(),
"__redeemShares: _additionalAssets contains duplicates"
);
require(_assetsToSkip.isUniqueSet(), "__redeemShares: _assetsToSkip contains duplicates");
IVault vaultProxyContract = IVault(vaultProxy);
// Only apply the sharesActionTimelock when a migration is not pendingif (!IDispatcher(DISPATCHER).hasMigrationRequest(address(vaultProxyContract))) {
__assertSharesActionNotTimelocked(_redeemer);
acctToLastSharesAction[_redeemer] =block.timestamp;
}
// When a fund is paused, settling fees will be skippedif (!__fundIsPaused()) {
// Note that if a fee with `SettlementType.Direct` is charged here (i.e., not `Mint`),// then those fee shares will be transferred from the user's balance rather// than reallocated from the sharesQuantity being redeemed.
__preRedeemSharesHook(_redeemer, _sharesQuantity);
}
// Check the shares quantity against the user's balance after settling fees
ERC20 sharesContract = ERC20(address(vaultProxyContract));
require(
_sharesQuantity <= sharesContract.balanceOf(_redeemer),
"__redeemShares: Insufficient shares"
);
// Parse the payout assets given optional params to add or skip assets.// Note that there is no validation that the _additionalAssets are known assets to// the protocol. This means that the redeemer could specify a malicious asset,// but since all state-changing, user-callable functions on this contract share the// non-reentrant modifier, there is nowhere to perform a reentrancy attack.
payoutAssets_ = __parseRedemptionPayoutAssets(
vaultProxyContract.getTrackedAssets(),
_additionalAssets,
_assetsToSkip
);
require(payoutAssets_.length>0, "__redeemShares: No payout assets");
// Destroy the shares.// Must get the shares supply before doing so.uint256 sharesSupply = sharesContract.totalSupply();
vaultProxyContract.burnShares(_redeemer, _sharesQuantity);
// Calculate and transfer payout asset amounts due to redeemer
payoutAmounts_ =newuint256[](payoutAssets_.length);
address denominationAssetCopy = denominationAsset;
for (uint256 i; i < payoutAssets_.length; i++) {
uint256 assetBalance = __finalizeIfSynthAndGetAssetBalance(
address(vaultProxyContract),
payoutAssets_[i],
true
);
// If all remaining shares are being redeemed, the logic changes slightlyif (_sharesQuantity == sharesSupply) {
payoutAmounts_[i] = assetBalance;
// Remove every tracked asset, except the denomination assetif (payoutAssets_[i] != denominationAssetCopy) {
vaultProxyContract.removeTrackedAsset(payoutAssets_[i]);
}
} else {
payoutAmounts_[i] = assetBalance.mul(_sharesQuantity).div(sharesSupply);
}
// Transfer payout asset to redeemerif (payoutAmounts_[i] >0) {
vaultProxyContract.withdrawAssetTo(payoutAssets_[i], _redeemer, payoutAmounts_[i]);
}
}
emit SharesRedeemed(_redeemer, _sharesQuantity, payoutAssets_, payoutAmounts_);
return (payoutAssets_, payoutAmounts_);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `denominationAsset` variable/// @return denominationAsset_ The `denominationAsset` variable valuefunctiongetDenominationAsset() externalviewoverridereturns (address denominationAsset_) {
return denominationAsset;
}
/// @notice Gets the routes for the various contracts used by all funds/// @return dispatcher_ The `DISPATCHER` variable value/// @return feeManager_ The `FEE_MANAGER` variable value/// @return fundDeployer_ The `FUND_DEPLOYER` variable value/// @return integrationManager_ The `INTEGRATION_MANAGER` variable value/// @return policyManager_ The `POLICY_MANAGER` variable value/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable value/// @return valueInterpreter_ The `VALUE_INTERPRETER` variable valuefunctiongetLibRoutes()
externalviewreturns (address dispatcher_,
address feeManager_,
address fundDeployer_,
address integrationManager_,
address policyManager_,
address primitivePriceFeed_,
address valueInterpreter_
)
{
return (
DISPATCHER,
FEE_MANAGER,
FUND_DEPLOYER,
INTEGRATION_MANAGER,
POLICY_MANAGER,
PRIMITIVE_PRICE_FEED,
VALUE_INTERPRETER
);
}
/// @notice Gets the `overridePause` variable/// @return overridePause_ The `overridePause` variable valuefunctiongetOverridePause() externalviewreturns (bool overridePause_) {
return overridePause;
}
/// @notice Gets the `sharesActionTimelock` variable/// @return sharesActionTimelock_ The `sharesActionTimelock` variable valuefunctiongetSharesActionTimelock() externalviewreturns (uint256 sharesActionTimelock_) {
return sharesActionTimelock;
}
/// @notice Gets the `vaultProxy` variable/// @return vaultProxy_ The `vaultProxy` variable valuefunctiongetVaultProxy() externalviewoverridereturns (address vaultProxy_) {
return vaultProxy;
}
}
Contract Source Code
File 23 of 138: ComptrollerProxy.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../utils/Proxy.sol";
/// @title ComptrollerProxy Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A proxy contract for all ComptrollerProxy instancescontractComptrollerProxyisProxy{
constructor(bytesmemory _constructData, address _comptrollerLib)
publicProxy(_constructData, _comptrollerLib)
{}
}
Contract Source Code
File 24 of 138: Context.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (addresspayable) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytesmemory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691returnmsg.data;
}
}
Contract Source Code
File 25 of 138: Dispatcher.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/IMigrationHookHandler.sol";
import"../utils/IMigratableVault.sol";
import"../vault/VaultProxy.sol";
import"./IDispatcher.sol";
/// @title Dispatcher Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice The top-level contract linking multiple releases./// It handles the deployment of new VaultProxy instances,/// and the regulation of fund migration from a previous release to the current one./// It can also be referred to for access-control based on this contract's owner./// @dev DO NOT EDIT CONTRACTcontractDispatcherisIDispatcher{
eventCurrentFundDeployerSet(address prevFundDeployer, address nextFundDeployer);
eventMigrationCancelled(addressindexed vaultProxy,
addressindexed prevFundDeployer,
addressindexed nextFundDeployer,
address nextVaultAccessor,
address nextVaultLib,
uint256 executableTimestamp
);
eventMigrationExecuted(addressindexed vaultProxy,
addressindexed prevFundDeployer,
addressindexed nextFundDeployer,
address nextVaultAccessor,
address nextVaultLib,
uint256 executableTimestamp
);
eventMigrationSignaled(addressindexed vaultProxy,
addressindexed prevFundDeployer,
addressindexed nextFundDeployer,
address nextVaultAccessor,
address nextVaultLib,
uint256 executableTimestamp
);
eventMigrationTimelockSet(uint256 prevTimelock, uint256 nextTimelock);
eventNominatedOwnerSet(addressindexed nominatedOwner);
eventNominatedOwnerRemoved(addressindexed nominatedOwner);
eventOwnershipTransferred(addressindexed prevOwner, addressindexed nextOwner);
eventMigrationInCancelHookFailed(bytes failureReturnData,
addressindexed vaultProxy,
addressindexed prevFundDeployer,
addressindexed nextFundDeployer,
address nextVaultAccessor,
address nextVaultLib
);
eventMigrationOutHookFailed(bytes failureReturnData,
IMigrationHookHandler.MigrationOutHook hook,
addressindexed vaultProxy,
addressindexed prevFundDeployer,
addressindexed nextFundDeployer,
address nextVaultAccessor,
address nextVaultLib
);
eventSharesTokenSymbolSet(string _nextSymbol);
eventVaultProxyDeployed(addressindexed fundDeployer,
addressindexed owner,
address vaultProxy,
addressindexed vaultLib,
address vaultAccessor,
string fundName
);
structMigrationRequest {
address nextFundDeployer;
address nextVaultAccessor;
address nextVaultLib;
uint256 executableTimestamp;
}
addressprivate currentFundDeployer;
addressprivate nominatedOwner;
addressprivate owner;
uint256private migrationTimelock;
stringprivate sharesTokenSymbol;
mapping(address=>address) private vaultProxyToFundDeployer;
mapping(address=> MigrationRequest) private vaultProxyToMigrationRequest;
modifieronlyCurrentFundDeployer() {
require(
msg.sender== currentFundDeployer,
"Only the current FundDeployer can call this function"
);
_;
}
modifieronlyOwner() {
require(msg.sender== owner, "Only the contract owner can call this function");
_;
}
constructor() public{
migrationTimelock =2days;
owner =msg.sender;
sharesTokenSymbol ="ENZF";
}
/////////////// GENERAL ////////////////// @notice Sets a new `symbol` value for VaultProxy instances/// @param _nextSymbol The symbol value to setfunctionsetSharesTokenSymbol(stringcalldata _nextSymbol) externaloverrideonlyOwner{
sharesTokenSymbol = _nextSymbol;
emit SharesTokenSymbolSet(_nextSymbol);
}
////////////////////// ACCESS CONTROL ///////////////////////// @notice Claim ownership of the contractfunctionclaimOwnership() externaloverride{
address nextOwner = nominatedOwner;
require(
msg.sender== nextOwner,
"claimOwnership: Only the nominatedOwner can call this function"
);
delete nominatedOwner;
address prevOwner = owner;
owner = nextOwner;
emit OwnershipTransferred(prevOwner, nextOwner);
}
/// @notice Revoke the nomination of a new contract ownerfunctionremoveNominatedOwner() externaloverrideonlyOwner{
address removedNominatedOwner = nominatedOwner;
require(
removedNominatedOwner !=address(0),
"removeNominatedOwner: There is no nominated owner"
);
delete nominatedOwner;
emit NominatedOwnerRemoved(removedNominatedOwner);
}
/// @notice Set a new FundDeployer for use within the contract/// @param _nextFundDeployer The address of the FundDeployer contractfunctionsetCurrentFundDeployer(address _nextFundDeployer) externaloverrideonlyOwner{
require(
_nextFundDeployer !=address(0),
"setCurrentFundDeployer: _nextFundDeployer cannot be empty"
);
require(
__isContract(_nextFundDeployer),
"setCurrentFundDeployer: Non-contract _nextFundDeployer"
);
address prevFundDeployer = currentFundDeployer;
require(
_nextFundDeployer != prevFundDeployer,
"setCurrentFundDeployer: _nextFundDeployer is already currentFundDeployer"
);
currentFundDeployer = _nextFundDeployer;
emit CurrentFundDeployerSet(prevFundDeployer, _nextFundDeployer);
}
/// @notice Nominate a new contract owner/// @param _nextNominatedOwner The account to nominate/// @dev Does not prohibit overwriting the current nominatedOwnerfunctionsetNominatedOwner(address _nextNominatedOwner) externaloverrideonlyOwner{
require(
_nextNominatedOwner !=address(0),
"setNominatedOwner: _nextNominatedOwner cannot be empty"
);
require(
_nextNominatedOwner != owner,
"setNominatedOwner: _nextNominatedOwner is already the owner"
);
require(
_nextNominatedOwner != nominatedOwner,
"setNominatedOwner: _nextNominatedOwner is already nominated"
);
nominatedOwner = _nextNominatedOwner;
emit NominatedOwnerSet(_nextNominatedOwner);
}
/// @dev Helper to check whether an address is a deployed contractfunction__isContract(address _who) privateviewreturns (bool isContract_) {
uint256 size;
assembly {
size :=extcodesize(_who)
}
return size >0;
}
////////////////// DEPLOYMENT ///////////////////// @notice Deploys a VaultProxy/// @param _vaultLib The VaultLib library with which to instantiate the VaultProxy/// @param _owner The account to set as the VaultProxy's owner/// @param _vaultAccessor The account to set as the VaultProxy's permissioned accessor/// @param _fundName The name of the fund/// @dev Input validation should be handled by the VaultProxy during deploymentfunctiondeployVaultProxy(address _vaultLib,
address _owner,
address _vaultAccessor,
stringcalldata _fundName
) externaloverrideonlyCurrentFundDeployerreturns (address vaultProxy_) {
require(__isContract(_vaultAccessor), "deployVaultProxy: Non-contract _vaultAccessor");
bytesmemory constructData =abi.encodeWithSelector(
IMigratableVault.init.selector,
_owner,
_vaultAccessor,
_fundName
);
vaultProxy_ =address(new VaultProxy(constructData, _vaultLib));
address fundDeployer =msg.sender;
vaultProxyToFundDeployer[vaultProxy_] = fundDeployer;
emit VaultProxyDeployed(
fundDeployer,
_owner,
vaultProxy_,
_vaultLib,
_vaultAccessor,
_fundName
);
return vaultProxy_;
}
////////////////// MIGRATIONS ///////////////////// @notice Cancels a pending migration request/// @param _vaultProxy The VaultProxy contract for which to cancel the migration request/// @param _bypassFailure True if a failure in either migration hook should be ignored/// @dev Because this function must also be callable by a permissioned migrator, it has an/// extra migration hook to the nextFundDeployer for the case where cancelMigration()/// is called directly (rather than via the nextFundDeployer).functioncancelMigration(address _vaultProxy, bool _bypassFailure) externaloverride{
MigrationRequest memory request = vaultProxyToMigrationRequest[_vaultProxy];
address nextFundDeployer = request.nextFundDeployer;
require(nextFundDeployer !=address(0), "cancelMigration: No migration request exists");
// TODO: confirm that if canMigrate() does not exist but the caller is a valid FundDeployer, this still works.require(
msg.sender== nextFundDeployer || IMigratableVault(_vaultProxy).canMigrate(msg.sender),
"cancelMigration: Not an allowed caller"
);
address prevFundDeployer = vaultProxyToFundDeployer[_vaultProxy];
address nextVaultAccessor = request.nextVaultAccessor;
address nextVaultLib = request.nextVaultLib;
uint256 executableTimestamp = request.executableTimestamp;
delete vaultProxyToMigrationRequest[_vaultProxy];
__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook.PostCancel,
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
_bypassFailure
);
__invokeMigrationInCancelHook(
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
_bypassFailure
);
emit MigrationCancelled(
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
executableTimestamp
);
}
/// @notice Executes a pending migration request/// @param _vaultProxy The VaultProxy contract for which to execute the migration request/// @param _bypassFailure True if a failure in either migration hook should be ignoredfunctionexecuteMigration(address _vaultProxy, bool _bypassFailure) externaloverride{
MigrationRequest memory request = vaultProxyToMigrationRequest[_vaultProxy];
address nextFundDeployer = request.nextFundDeployer;
require(
nextFundDeployer !=address(0),
"executeMigration: No migration request exists for _vaultProxy"
);
require(
msg.sender== nextFundDeployer,
"executeMigration: Only the target FundDeployer can call this function"
);
require(
nextFundDeployer == currentFundDeployer,
"executeMigration: The target FundDeployer is no longer the current FundDeployer"
);
uint256 executableTimestamp = request.executableTimestamp;
require(
block.timestamp>= executableTimestamp,
"executeMigration: The migration timelock has not elapsed"
);
address prevFundDeployer = vaultProxyToFundDeployer[_vaultProxy];
address nextVaultAccessor = request.nextVaultAccessor;
address nextVaultLib = request.nextVaultLib;
__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook.PreMigrate,
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
_bypassFailure
);
// Upgrade the VaultProxy to a new VaultLib and update the accessor via the new VaultLib
IMigratableVault(_vaultProxy).setVaultLib(nextVaultLib);
IMigratableVault(_vaultProxy).setAccessor(nextVaultAccessor);
// Update the FundDeployer that migrated the VaultProxy
vaultProxyToFundDeployer[_vaultProxy] = nextFundDeployer;
// Remove the migration requestdelete vaultProxyToMigrationRequest[_vaultProxy];
__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook.PostMigrate,
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
_bypassFailure
);
emit MigrationExecuted(
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
nextVaultAccessor,
nextVaultLib,
executableTimestamp
);
}
/// @notice Sets a new migration timelock/// @param _nextTimelock The number of seconds for the new timelockfunctionsetMigrationTimelock(uint256 _nextTimelock) externaloverrideonlyOwner{
uint256 prevTimelock = migrationTimelock;
require(
_nextTimelock != prevTimelock,
"setMigrationTimelock: _nextTimelock is the current timelock"
);
migrationTimelock = _nextTimelock;
emit MigrationTimelockSet(prevTimelock, _nextTimelock);
}
/// @notice Signals a migration by creating a migration request/// @param _vaultProxy The VaultProxy contract for which to signal migration/// @param _nextVaultAccessor The account that will be the next `accessor` on the VaultProxy/// @param _nextVaultLib The next VaultLib library contract address to set on the VaultProxy/// @param _bypassFailure True if a failure in either migration hook should be ignoredfunctionsignalMigration(address _vaultProxy,
address _nextVaultAccessor,
address _nextVaultLib,
bool _bypassFailure
) externaloverrideonlyCurrentFundDeployer{
require(
__isContract(_nextVaultAccessor),
"signalMigration: Non-contract _nextVaultAccessor"
);
address prevFundDeployer = vaultProxyToFundDeployer[_vaultProxy];
require(prevFundDeployer !=address(0), "signalMigration: _vaultProxy does not exist");
address nextFundDeployer =msg.sender;
require(
nextFundDeployer != prevFundDeployer,
"signalMigration: Can only migrate to a new FundDeployer"
);
__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook.PreSignal,
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib,
_bypassFailure
);
uint256 executableTimestamp =block.timestamp+ migrationTimelock;
vaultProxyToMigrationRequest[_vaultProxy] = MigrationRequest({
nextFundDeployer: nextFundDeployer,
nextVaultAccessor: _nextVaultAccessor,
nextVaultLib: _nextVaultLib,
executableTimestamp: executableTimestamp
});
__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook.PostSignal,
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib,
_bypassFailure
);
emit MigrationSignaled(
_vaultProxy,
prevFundDeployer,
nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib,
executableTimestamp
);
}
/// @dev Helper to invoke a MigrationInCancelHook on the next FundDeployer being "migrated in" to,/// which can optionally be implemented on the FundDeployerfunction__invokeMigrationInCancelHook(address _vaultProxy,
address _prevFundDeployer,
address _nextFundDeployer,
address _nextVaultAccessor,
address _nextVaultLib,
bool _bypassFailure
) private{
(bool success, bytesmemory returnData) = _nextFundDeployer.call(
abi.encodeWithSelector(
IMigrationHookHandler.invokeMigrationInCancelHook.selector,
_vaultProxy,
_prevFundDeployer,
_nextVaultAccessor,
_nextVaultLib
)
);
if (!success) {
require(
_bypassFailure,
string(abi.encodePacked("MigrationOutCancelHook: ", returnData))
);
emit MigrationInCancelHookFailed(
returnData,
_vaultProxy,
_prevFundDeployer,
_nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib
);
}
}
/// @dev Helper to invoke a IMigrationHookHandler.MigrationOutHook on the previous FundDeployer being "migrated out" of,/// which can optionally be implemented on the FundDeployerfunction__invokeMigrationOutHook(
IMigrationHookHandler.MigrationOutHook _hook,
address _vaultProxy,
address _prevFundDeployer,
address _nextFundDeployer,
address _nextVaultAccessor,
address _nextVaultLib,
bool _bypassFailure
) private{
(bool success, bytesmemory returnData) = _prevFundDeployer.call(
abi.encodeWithSelector(
IMigrationHookHandler.invokeMigrationOutHook.selector,
_hook,
_vaultProxy,
_nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib
)
);
if (!success) {
require(
_bypassFailure,
string(abi.encodePacked(__migrationOutHookFailureReasonPrefix(_hook), returnData))
);
emit MigrationOutHookFailed(
returnData,
_hook,
_vaultProxy,
_prevFundDeployer,
_nextFundDeployer,
_nextVaultAccessor,
_nextVaultLib
);
}
}
/// @dev Helper to return a revert reason string prefix for a given MigrationOutHookfunction__migrationOutHookFailureReasonPrefix(IMigrationHookHandler.MigrationOutHook _hook)
privatepurereturns (stringmemory failureReasonPrefix_)
{
if (_hook == IMigrationHookHandler.MigrationOutHook.PreSignal) {
return"MigrationOutHook.PreSignal: ";
}
if (_hook == IMigrationHookHandler.MigrationOutHook.PostSignal) {
return"MigrationOutHook.PostSignal: ";
}
if (_hook == IMigrationHookHandler.MigrationOutHook.PreMigrate) {
return"MigrationOutHook.PreMigrate: ";
}
if (_hook == IMigrationHookHandler.MigrationOutHook.PostMigrate) {
return"MigrationOutHook.PostMigrate: ";
}
if (_hook == IMigrationHookHandler.MigrationOutHook.PostCancel) {
return"MigrationOutHook.PostCancel: ";
}
return"";
}
///////////////////// STATE GETTERS /////////////////////// Provides several potentially helpful getters that are not strictly necessary/// @notice Gets the current FundDeployer that is allowed to deploy and migrate funds/// @return currentFundDeployer_ The current FundDeployer contract addressfunctiongetCurrentFundDeployer()
externalviewoverridereturns (address currentFundDeployer_)
{
return currentFundDeployer;
}
/// @notice Gets the FundDeployer with which a given VaultProxy is associated/// @param _vaultProxy The VaultProxy instance/// @return fundDeployer_ The FundDeployer contract addressfunctiongetFundDeployerForVaultProxy(address _vaultProxy)
externalviewoverridereturns (address fundDeployer_)
{
return vaultProxyToFundDeployer[_vaultProxy];
}
/// @notice Gets the details of a pending migration request for a given VaultProxy/// @param _vaultProxy The VaultProxy instance/// @return nextFundDeployer_ The FundDeployer contract address from which the migration/// request was made/// @return nextVaultAccessor_ The account that will be the next `accessor` on the VaultProxy/// @return nextVaultLib_ The next VaultLib library contract address to set on the VaultProxy/// @return executableTimestamp_ The timestamp at which the migration request can be executedfunctiongetMigrationRequestDetailsForVaultProxy(address _vaultProxy)
externalviewoverridereturns (address nextFundDeployer_,
address nextVaultAccessor_,
address nextVaultLib_,
uint256 executableTimestamp_
)
{
MigrationRequest memory r = vaultProxyToMigrationRequest[_vaultProxy];
if (r.executableTimestamp >0) {
return (
r.nextFundDeployer,
r.nextVaultAccessor,
r.nextVaultLib,
r.executableTimestamp
);
}
}
/// @notice Gets the amount of time that must pass between signaling and executing a migration/// @return migrationTimelock_ The timelock value (in seconds)functiongetMigrationTimelock() externalviewoverridereturns (uint256 migrationTimelock_) {
return migrationTimelock;
}
/// @notice Gets the account that is nominated to be the next owner of this contract/// @return nominatedOwner_ The account that is nominated to be the ownerfunctiongetNominatedOwner() externalviewoverridereturns (address nominatedOwner_) {
return nominatedOwner;
}
/// @notice Gets the owner of this contract/// @return owner_ The account that is the ownerfunctiongetOwner() externalviewoverridereturns (address owner_) {
return owner;
}
/// @notice Gets the shares token `symbol` value for use in VaultProxy instances/// @return sharesTokenSymbol_ The `symbol` valuefunctiongetSharesTokenSymbol()
externalviewoverridereturns (stringmemory sharesTokenSymbol_)
{
return sharesTokenSymbol;
}
/// @notice Gets the time remaining until the migration request of a given VaultProxy can be executed/// @param _vaultProxy The VaultProxy instance/// @return secondsRemaining_ The number of seconds remaining on the timelockfunctiongetTimelockRemainingForMigrationRequest(address _vaultProxy)
externalviewoverridereturns (uint256 secondsRemaining_)
{
uint256 executableTimestamp = vaultProxyToMigrationRequest[_vaultProxy]
.executableTimestamp;
if (executableTimestamp ==0) {
return0;
}
if (block.timestamp>= executableTimestamp) {
return0;
}
return executableTimestamp -block.timestamp;
}
/// @notice Checks whether a migration request that is executable exists for a given VaultProxy/// @param _vaultProxy The VaultProxy instance/// @return hasExecutableRequest_ True if a migration request exists and is executablefunctionhasExecutableMigrationRequest(address _vaultProxy)
externalviewoverridereturns (bool hasExecutableRequest_)
{
uint256 executableTimestamp = vaultProxyToMigrationRequest[_vaultProxy]
.executableTimestamp;
return executableTimestamp >0&&block.timestamp>= executableTimestamp;
}
/// @notice Checks whether a migration request exists for a given VaultProxy/// @param _vaultProxy The VaultProxy instance/// @return hasMigrationRequest_ True if a migration request existsfunctionhasMigrationRequest(address _vaultProxy)
externalviewoverridereturns (bool hasMigrationRequest_)
{
return vaultProxyToMigrationRequest[_vaultProxy].executableTimestamp >0;
}
}
Contract Source Code
File 26 of 138: DispatcherOwnerMixin.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../persistent/dispatcher/IDispatcher.sol";
/// @title DispatcherOwnerMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract that defers ownership to the owner of DispatcherabstractcontractDispatcherOwnerMixin{
addressinternalimmutable DISPATCHER;
modifieronlyDispatcherOwner() {
require(
msg.sender== getOwner(),
"onlyDispatcherOwner: Only the Dispatcher owner can call this function"
);
_;
}
constructor(address _dispatcher) public{
DISPATCHER = _dispatcher;
}
/// @notice Gets the owner of this contract/// @return owner_ The owner/// @dev Ownership is deferred to the owner of the Dispatcher contractfunctiongetOwner() publicviewreturns (address owner_) {
return IDispatcher(DISPATCHER).getOwner();
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `DISPATCHER` variable/// @return dispatcher_ The `DISPATCHER` variable valuefunctiongetDispatcher() externalviewreturns (address dispatcher_) {
return DISPATCHER;
}
}
Contract Source Code
File 27 of 138: ERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;import"../../GSN/Context.sol";
import"./IERC20.sol";
import"../../math/SafeMath.sol";
import"../../utils/Address.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/contractERC20isContext, IERC20{
usingSafeMathforuint256;
usingAddressforaddress;
mapping (address=>uint256) private _balances;
mapping (address=>mapping (address=>uint256)) private _allowances;
uint256private _totalSupply;
stringprivate _name;
stringprivate _symbol;
uint8private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/constructor (stringmemory name, stringmemory symbol) public{
_name = name;
_symbol = symbol;
_decimals =18;
}
/**
* @dev Returns the name of the token.
*/functionname() publicviewreturns (stringmemory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/functionsymbol() publicviewreturns (stringmemory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/functiondecimals() publicviewreturns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/functiontotalSupply() publicviewoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/functionbalanceOf(address account) publicviewoverridereturns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/functiontransfer(address recipient, uint256 amount) publicvirtualoverridereturns (bool) {
_transfer(_msgSender(), recipient, amount);
returntrue;
}
/**
* @dev See {IERC20-allowance}.
*/functionallowance(address owner, address spender) publicviewvirtualoverridereturns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionapprove(address spender, uint256 amount) publicvirtualoverridereturns (bool) {
_approve(_msgSender(), spender, amount);
returntrue;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/functiontransferFrom(address sender, address recipient, uint256 amount) publicvirtualoverridereturns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
returntrue;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionincreaseAllowance(address spender, uint256 addedValue) publicvirtualreturns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
returntrue;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/functiondecreaseAllowance(address spender, uint256 subtractedValue) publicvirtualreturns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
returntrue;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/function_transfer(address sender, address recipient, uint256 amount) internalvirtual{
require(sender !=address(0), "ERC20: transfer from the zero address");
require(recipient !=address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/function_mint(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/function_burn(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/function_approve(address owner, address spender, uint256 amount) internalvirtual{
require(owner !=address(0), "ERC20: approve from the zero address");
require(spender !=address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/function_setupDecimals(uint8 decimals_) internal{
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom, address to, uint256 amount) internalvirtual{ }
}
Contract Source Code
File 28 of 138: ERC20Burnable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;import"../../GSN/Context.sol";
import"./ERC20.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/abstractcontractERC20BurnableisContext, ERC20{
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/functionburn(uint256 amount) publicvirtual{
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/functionburnFrom(address account, uint256 amount) publicvirtual{
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
}
Contract Source Code
File 29 of 138: EntranceRateBurnFee.sol
// 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.
*/pragmasolidity 0.6.12;import"./utils/EntranceRateFeeBase.sol";
/// @title EntranceRateBurnFee Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice An EntranceRateFee that burns the fee sharescontractEntranceRateBurnFeeisEntranceRateFeeBase{
constructor(address _feeManager)
publicEntranceRateFeeBase(_feeManager, IFeeManager.SettlementType.Burn)
{}
/// @notice Provides a constant string identifier for a fee/// @return identifier_ The identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ENTRANCE_RATE_BURN";
}
}
Contract Source Code
File 30 of 138: EntranceRateDirectFee.sol
// 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.
*/pragmasolidity 0.6.12;import"./utils/EntranceRateFeeBase.sol";
/// @title EntranceRateDirectFee Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice An EntranceRateFee that transfers the fee shares to the fund managercontractEntranceRateDirectFeeisEntranceRateFeeBase{
constructor(address _feeManager)
publicEntranceRateFeeBase(_feeManager, IFeeManager.SettlementType.Direct)
{}
/// @notice Provides a constant string identifier for a fee/// @return identifier_ The identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ENTRANCE_RATE_DIRECT";
}
}
Contract Source Code
File 31 of 138: EntranceRateFeeBase.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"./FeeBase.sol";
/// @title EntranceRateFeeBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Calculates a fee based on a rate to be charged to an investor upon entering a fundabstractcontractEntranceRateFeeBaseisFeeBase{
usingSafeMathforuint256;
eventFundSettingsAdded(addressindexed comptrollerProxy, uint256 rate);
eventSettled(addressindexed comptrollerProxy, addressindexed payer, uint256 sharesQuantity);
uint256privateconstant RATE_DIVISOR =10**18;
IFeeManager.SettlementType privateimmutable SETTLEMENT_TYPE;
mapping(address=>uint256) private comptrollerProxyToRate;
constructor(address _feeManager, IFeeManager.SettlementType _settlementType)
publicFeeBase(_feeManager)
{
require(
_settlementType == IFeeManager.SettlementType.Burn ||
_settlementType == IFeeManager.SettlementType.Direct,
"constructor: Invalid _settlementType"
);
SETTLEMENT_TYPE = _settlementType;
}
// EXTERNAL FUNCTIONS/// @notice Add the fee settings for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _settingsData Encoded settings to apply to the policy for a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _settingsData)
externaloverrideonlyFeeManager{
uint256 rate =abi.decode(_settingsData, (uint256));
require(rate >0, "addFundSettings: Fee rate must be >0");
comptrollerProxyToRate[_comptrollerProxy] = rate;
emit FundSettingsAdded(_comptrollerProxy, rate);
}
/// @notice Gets the hooks that are implemented by the fee/// @return implementedHooksForSettle_ The hooks during which settle() is implemented/// @return implementedHooksForUpdate_ The hooks during which update() is implemented/// @return usesGavOnSettle_ True if GAV is used during the settle() implementation/// @return usesGavOnUpdate_ True if GAV is used during the update() implementation/// @dev Used only during fee registrationfunctionimplementedHooks()
externalviewoverridereturns (
IFeeManager.FeeHook[] memory implementedHooksForSettle_,
IFeeManager.FeeHook[] memory implementedHooksForUpdate_,
bool usesGavOnSettle_,
bool usesGavOnUpdate_
)
{
implementedHooksForSettle_ =new IFeeManager.FeeHook[](1);
implementedHooksForSettle_[0] = IFeeManager.FeeHook.PostBuyShares;
return (implementedHooksForSettle_, new IFeeManager.FeeHook[](0), false, false);
}
/// @notice Settles the fee/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _settlementData Encoded args to use in calculating the settlement/// @return settlementType_ The type of settlement/// @return payer_ The payer of shares due/// @return sharesDue_ The amount of shares duefunctionsettle(address _comptrollerProxy,
address,
IFeeManager.FeeHook,
bytescalldata _settlementData,
uint256)
externaloverrideonlyFeeManagerreturns (
IFeeManager.SettlementType settlementType_,
address payer_,
uint256 sharesDue_
)
{
uint256 sharesBought;
(payer_, , sharesBought) = __decodePostBuySharesSettlementData(_settlementData);
uint256 rate = comptrollerProxyToRate[_comptrollerProxy];
sharesDue_ = sharesBought.mul(rate).div(RATE_DIVISOR.add(rate));
if (sharesDue_ ==0) {
return (IFeeManager.SettlementType.None, address(0), 0);
}
emit Settled(_comptrollerProxy, payer_, sharesDue_);
return (SETTLEMENT_TYPE, payer_, sharesDue_);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `rate` variable for a fund/// @param _comptrollerProxy The ComptrollerProxy contract for the fund/// @return rate_ The `rate` variable valuefunctiongetRateForFund(address _comptrollerProxy) externalviewreturns (uint256 rate_) {
return comptrollerProxyToRate[_comptrollerProxy];
}
/// @notice Gets the `SETTLEMENT_TYPE` variable/// @return settlementType_ The `SETTLEMENT_TYPE` variable valuefunctiongetSettlementType()
externalviewreturns (IFeeManager.SettlementType settlementType_)
{
return SETTLEMENT_TYPE;
}
}
Contract Source Code
File 32 of 138: EnumerableSet.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/libraryEnumerableSet{
// To implement this library for multiple types with as little code// repetition as possible, we write it in terms of a generic Set type with// bytes32 values.// The Set implementation uses private functions, and user-facing// implementations (such as AddressSet) are just wrappers around the// underlying Set.// This means that we can only create new EnumerableSets for types that fit// in bytes32.structSet {
// Storage of set valuesbytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0// means a value is not in the set.mapping (bytes32=>uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/function_add(Set storage set, bytes32 value) privatereturns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/function_remove(Set storage set, bytes32 value) privatereturns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slotuint256 valueIndex = set._indexes[value];
if (valueIndex !=0) { // Equivalent to contains(set, value)// To delete an element from the _values array in O(1), we swap the element to delete with the last one in// the array, and then remove the last element (sometimes called as 'swap and pop').// This modifies the order of the array, as noted in {at}.uint256 toDeleteIndex = valueIndex -1;
uint256 lastIndex = set._values.length-1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex +1; // All indexes are 1-based// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slotdelete set._indexes[value];
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/function_contains(Set storage set, bytes32 value) privateviewreturns (bool) {
return set._indexes[value] !=0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/function_length(Set storage set) privateviewreturns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/function_at(Set storage set, uint256 index) privateviewreturns (bytes32) {
require(set._values.length> index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSetstructAddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(AddressSet storage set, address value) internalreturns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(AddressSet storage set, address value) internalreturns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(AddressSet storage set, address value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(AddressSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(AddressSet storage set, uint256 index) internalviewreturns (address) {
returnaddress(uint256(_at(set._inner, index)));
}
// UintSetstructUintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(UintSet storage set, uint256 value) internalreturns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(UintSet storage set, uint256 value) internalreturns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(UintSet storage set, uint256 value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/functionlength(UintSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(UintSet storage set, uint256 index) internalviewreturns (uint256) {
returnuint256(_at(set._inner, index));
}
}
Contract Source Code
File 33 of 138: EthConstantMixin.sol
// 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.
*/pragmasolidity 0.6.12;abstractcontractEthConstantMixin{
addresspublicconstant ETH_ADDRESS =0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}
Contract Source Code
File 34 of 138: ExtensionBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../core/fund/comptroller/IComptroller.sol";
import"../../core/fund/vault/IVault.sol";
import"../IExtension.sol";
/// @title ExtensionBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Base class for an extensionabstractcontractExtensionBaseisIExtension{
mapping(address=>address) internal comptrollerProxyToVaultProxy;
/// @notice Allows extension to run logic during fund activation/// @dev Unimplemented by default, may be overridden.functionactivateForFund(bool) externalvirtualoverride{
return;
}
/// @notice Allows extension to run logic during fund deactivation (destruct)/// @dev Unimplemented by default, may be overridden.functiondeactivateForFund() externalvirtualoverride{
return;
}
/// @notice Receives calls from ComptrollerLib.callOnExtension()/// and dispatches the appropriate action/// @dev Unimplemented by default, may be overridden.functionreceiveCallFromComptroller(address,
uint256,
bytescalldata) externalvirtualoverride{
revert("receiveCallFromComptroller: Unimplemented for Extension");
}
/// @notice Allows extension to run logic during fund configuration/// @dev Unimplemented by default, may be overridden.functionsetConfigForFund(bytescalldata) externalvirtualoverride{
return;
}
/// @dev Helper to validate a ComptrollerProxy-VaultProxy relation, which we store for both/// gas savings and to guarantee a spoofed ComptrollerProxy does not change getVaultProxy()./// Will revert without reason if the expected interfaces do not exist.function__setValidatedVaultProxy(address _comptrollerProxy)
internalreturns (address vaultProxy_)
{
require(
comptrollerProxyToVaultProxy[_comptrollerProxy] ==address(0),
"__setValidatedVaultProxy: Already set"
);
vaultProxy_ = IComptroller(_comptrollerProxy).getVaultProxy();
require(vaultProxy_ !=address(0), "__setValidatedVaultProxy: Missing vaultProxy");
require(
_comptrollerProxy == IVault(vaultProxy_).getAccessor(),
"__setValidatedVaultProxy: Not the VaultProxy accessor"
);
comptrollerProxyToVaultProxy[_comptrollerProxy] = vaultProxy_;
return vaultProxy_;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the verified VaultProxy for a given ComptrollerProxy/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return vaultProxy_ The VaultProxy of the fundfunctiongetVaultProxyForFund(address _comptrollerProxy)
publicviewreturns (address vaultProxy_)
{
return comptrollerProxyToVaultProxy[_comptrollerProxy];
}
}
Contract Source Code
File 35 of 138: FeeBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../IFee.sol";
/// @title FeeBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Abstract base contract for all feesabstractcontractFeeBaseisIFee{
addressinternalimmutable FEE_MANAGER;
modifieronlyFeeManager{
require(msg.sender== FEE_MANAGER, "Only the FeeManger can make this call");
_;
}
constructor(address _feeManager) public{
FEE_MANAGER = _feeManager;
}
/// @notice Allows Fee to run logic during fund activation/// @dev Unimplemented by default, may be overrode.functionactivateForFund(address, address) externalvirtualoverride{
return;
}
/// @notice Runs payout logic for a fee that utilizes shares outstanding as its settlement type/// @dev Returns false by default, can be overridden by feefunctionpayout(address, address) externalvirtualoverridereturns (bool) {
returnfalse;
}
/// @notice Update fee state after all settlement has occurred during a given fee hook/// @dev Unimplemented by default, can be overridden by feefunctionupdate(address,
address,
IFeeManager.FeeHook,
bytescalldata,
uint256) externalvirtualoverride{
return;
}
/// @notice Helper to parse settlement arguments from encoded data for PreBuyShares fee hookfunction__decodePreBuySharesSettlementData(bytesmemory _settlementData)
internalpurereturns (address buyer_,
uint256 investmentAmount_,
uint256 minSharesQuantity_
)
{
returnabi.decode(_settlementData, (address, uint256, uint256));
}
/// @notice Helper to parse settlement arguments from encoded data for PreRedeemShares fee hookfunction__decodePreRedeemSharesSettlementData(bytesmemory _settlementData)
internalpurereturns (address redeemer_, uint256 sharesQuantity_)
{
returnabi.decode(_settlementData, (address, uint256));
}
/// @notice Helper to parse settlement arguments from encoded data for PostBuyShares fee hookfunction__decodePostBuySharesSettlementData(bytesmemory _settlementData)
internalpurereturns (address buyer_,
uint256 investmentAmount_,
uint256 sharesBought_
)
{
returnabi.decode(_settlementData, (address, uint256, uint256));
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `FEE_MANAGER` variable/// @return feeManager_ The `FEE_MANAGER` variable valuefunctiongetFeeManager() externalviewreturns (address feeManager_) {
return FEE_MANAGER;
}
}
Contract Source Code
File 36 of 138: FeeManager.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/utils/EnumerableSet.sol";
import"../../core/fund/comptroller/IComptroller.sol";
import"../../core/fund/vault/IVault.sol";
import"../../utils/AddressArrayLib.sol";
import"../utils/ExtensionBase.sol";
import"../utils/FundDeployerOwnerMixin.sol";
import"../utils/PermissionedVaultActionMixin.sol";
import"./IFee.sol";
import"./IFeeManager.sol";
/// @title FeeManager Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Manages fees for fundscontractFeeManagerisIFeeManager,
ExtensionBase,
FundDeployerOwnerMixin,
PermissionedVaultActionMixin{
usingAddressArrayLibforaddress[];
usingEnumerableSetforEnumerableSet.AddressSet;
usingSafeMathforuint256;
eventAllSharesOutstandingForcePaidForFund(addressindexed comptrollerProxy,
address payee,
uint256 sharesDue
);
eventFeeDeregistered(addressindexed fee, stringindexed identifier);
eventFeeEnabledForFund(addressindexed comptrollerProxy,
addressindexed fee,
bytes settingsData
);
eventFeeRegistered(addressindexed fee,
stringindexed identifier,
FeeHook[] implementedHooksForSettle,
FeeHook[] implementedHooksForUpdate,
bool usesGavOnSettle,
bool usesGavOnUpdate
);
eventFeeSettledForFund(addressindexed comptrollerProxy,
addressindexed fee,
SettlementType indexed settlementType,
address payer,
address payee,
uint256 sharesDue
);
eventSharesOutstandingPaidForFund(addressindexed comptrollerProxy,
addressindexed fee,
uint256 sharesDue
);
eventFeesRecipientSetForFund(addressindexed comptrollerProxy,
address prevFeesRecipient,
address nextFeesRecipient
);
EnumerableSet.AddressSet private registeredFees;
mapping(address=>bool) private feeToUsesGavOnSettle;
mapping(address=>bool) private feeToUsesGavOnUpdate;
mapping(address=>mapping(FeeHook =>bool)) private feeToHookToImplementsSettle;
mapping(address=>mapping(FeeHook =>bool)) private feeToHookToImplementsUpdate;
mapping(address=>address[]) private comptrollerProxyToFees;
mapping(address=>mapping(address=>uint256))
private comptrollerProxyToFeeToSharesOutstanding;
constructor(address _fundDeployer) publicFundDeployerOwnerMixin(_fundDeployer) {}
// EXTERNAL FUNCTIONS/// @notice Activate already-configured fees for use in the calling fundfunctionactivateForFund(bool) externaloverride{
address vaultProxy = __setValidatedVaultProxy(msg.sender);
address[] memory enabledFees = comptrollerProxyToFees[msg.sender];
for (uint256 i; i < enabledFees.length; i++) {
IFee(enabledFees[i]).activateForFund(msg.sender, vaultProxy);
}
}
/// @notice Deactivate fees for a fund/// @dev msg.sender is validated during __invokeHook()functiondeactivateForFund() externaloverride{
// Settle continuous fees one last time, but without calling Fee.update()
__invokeHook(msg.sender, IFeeManager.FeeHook.Continuous, "", 0, false);
// Force payout of remaining shares outstanding
__forcePayoutAllSharesOutstanding(msg.sender);
// Clean up storage
__deleteFundStorage(msg.sender);
}
/// @notice Receives a dispatched `callOnExtension` from a fund's ComptrollerProxy/// @param _actionId An ID representing the desired action/// @param _callArgs Encoded arguments specific to the _actionId/// @dev This is the only way to call a function on this contract that updates VaultProxy state./// For both of these actions, any caller is allowed, so we don't use the caller param.functionreceiveCallFromComptroller(address,
uint256 _actionId,
bytescalldata _callArgs
) externaloverride{
if (_actionId ==0) {
// Settle and update all continuous fees
__invokeHook(msg.sender, IFeeManager.FeeHook.Continuous, "", 0, true);
} elseif (_actionId ==1) {
__payoutSharesOutstandingForFees(msg.sender, _callArgs);
} else {
revert("receiveCallFromComptroller: Invalid _actionId");
}
}
/// @notice Enable and configure fees for use in the calling fund/// @param _configData Encoded config data/// @dev Caller is expected to be a valid ComptrollerProxy, but there isn't a need to validate./// The order of `fees` determines the order in which fees of the same FeeHook will be applied./// It is recommended to run ManagementFee before PerformanceFee in order to achieve precise/// PerformanceFee calcs.functionsetConfigForFund(bytescalldata _configData) externaloverride{
(address[] memory fees, bytes[] memory settingsData) =abi.decode(
_configData,
(address[], bytes[])
);
// Sanity checksrequire(
fees.length== settingsData.length,
"setConfigForFund: fees and settingsData array lengths unequal"
);
require(fees.isUniqueSet(), "setConfigForFund: fees cannot include duplicates");
// Enable each fee with settingsfor (uint256 i; i < fees.length; i++) {
require(isRegisteredFee(fees[i]), "setConfigForFund: Fee is not registered");
// Set fund config on fee
IFee(fees[i]).addFundSettings(msg.sender, settingsData[i]);
// Enable fee for fund
comptrollerProxyToFees[msg.sender].push(fees[i]);
emit FeeEnabledForFund(msg.sender, fees[i], settingsData[i]);
}
}
/// @notice Allows all fees for a particular FeeHook to implement settle() and update() logic/// @param _hook The FeeHook to invoke/// @param _settlementData The encoded settlement parameters specific to the FeeHook/// @param _gav The GAV for a fund if known in the invocating code, otherwise 0functioninvokeHook(
FeeHook _hook,
bytescalldata _settlementData,
uint256 _gav
) externaloverride{
__invokeHook(msg.sender, _hook, _settlementData, _gav, true);
}
// PRIVATE FUNCTIONS/// @dev Helper to destroy local storage to get gas refund,/// and to prevent further calls to fee managerfunction__deleteFundStorage(address _comptrollerProxy) private{
delete comptrollerProxyToFees[_comptrollerProxy];
delete comptrollerProxyToVaultProxy[_comptrollerProxy];
}
/// @dev Helper to force the payout of shares outstanding across all fees./// For the current release, all shares in the VaultProxy are assumed to be/// shares outstanding from fees. If not, then they were sent there by mistake/// and are otherwise unrecoverable. We can therefore take the VaultProxy's/// shares balance as the totalSharesOutstanding to payout to the fund owner.function__forcePayoutAllSharesOutstanding(address _comptrollerProxy) private{
address vaultProxy = getVaultProxyForFund(_comptrollerProxy);
uint256 totalSharesOutstanding = ERC20(vaultProxy).balanceOf(vaultProxy);
if (totalSharesOutstanding ==0) {
return;
}
// Destroy any shares outstanding storageaddress[] memory fees = comptrollerProxyToFees[_comptrollerProxy];
for (uint256 i; i < fees.length; i++) {
delete comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]];
}
// Distribute all shares outstanding to the fees recipientaddress payee = IVault(vaultProxy).getOwner();
__transferShares(_comptrollerProxy, vaultProxy, payee, totalSharesOutstanding);
emit AllSharesOutstandingForcePaidForFund(
_comptrollerProxy,
payee,
totalSharesOutstanding
);
}
/// @dev Helper to get the canonical value of GAV if not yet set and required by feefunction__getGavAsNecessary(address _comptrollerProxy,
address _fee,
uint256 _gavOrZero
) privatereturns (uint256 gav_) {
if (_gavOrZero ==0&& feeUsesGavOnUpdate(_fee)) {
// Assumes that any fee that requires GAV would need to revert if invalid or not finalbool gavIsValid;
(gav_, gavIsValid) = IComptroller(_comptrollerProxy).calcGav(true);
require(gavIsValid, "__getGavAsNecessary: Invalid GAV");
} else {
gav_ = _gavOrZero;
}
return gav_;
}
/// @dev Helper to run settle() on all enabled fees for a fund that implement a given hook, and then to/// optionally run update() on the same fees. This order allows fees an opportunity to update/// their local state after all VaultProxy state transitions (i.e., minting, burning,/// transferring shares) have finished. To optimize for the expensive operation of calculating/// GAV, once one fee requires GAV, we recycle that `gav` value for subsequent fees./// Assumes that _gav is either 0 or has already been validated.function__invokeHook(address _comptrollerProxy,
FeeHook _hook,
bytesmemory _settlementData,
uint256 _gavOrZero,
bool _updateFees
) private{
address[] memory fees = comptrollerProxyToFees[_comptrollerProxy];
if (fees.length==0) {
return;
}
address vaultProxy = getVaultProxyForFund(_comptrollerProxy);
// This check isn't strictly necessary, but its cost is insignificant,// and helps to preserve data integrity.require(vaultProxy !=address(0), "__invokeHook: Fund is not active");
// First, allow all fees to implement settle()uint256 gav = __settleFees(
_comptrollerProxy,
vaultProxy,
fees,
_hook,
_settlementData,
_gavOrZero
);
// Second, allow fees to implement update()// This function does not allow any further altering of VaultProxy state// (i.e., burning, minting, or transferring shares)if (_updateFees) {
__updateFees(_comptrollerProxy, vaultProxy, fees, _hook, _settlementData, gav);
}
}
/// @dev Helper to payout the shares outstanding for the specified fees./// Does not call settle() on fees./// Only callable via ComptrollerProxy.callOnExtension().function__payoutSharesOutstandingForFees(address _comptrollerProxy, bytesmemory _callArgs)
private{
address[] memory fees =abi.decode(_callArgs, (address[]));
address vaultProxy = getVaultProxyForFund(msg.sender);
uint256 sharesOutstandingDue;
for (uint256 i; i < fees.length; i++) {
if (!IFee(fees[i]).payout(_comptrollerProxy, vaultProxy)) {
continue;
}
uint256 sharesOutstandingForFee
= comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]];
if (sharesOutstandingForFee ==0) {
continue;
}
sharesOutstandingDue = sharesOutstandingDue.add(sharesOutstandingForFee);
// Delete shares outstanding and distribute from VaultProxy to the fees recipient
comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][fees[i]] =0;
emit SharesOutstandingPaidForFund(_comptrollerProxy, fees[i], sharesOutstandingForFee);
}
if (sharesOutstandingDue >0) {
__transferShares(
_comptrollerProxy,
vaultProxy,
IVault(vaultProxy).getOwner(),
sharesOutstandingDue
);
}
}
/// @dev Helper to settle a feefunction__settleFee(address _comptrollerProxy,
address _vaultProxy,
address _fee,
FeeHook _hook,
bytesmemory _settlementData,
uint256 _gav
) private{
(SettlementType settlementType, address payer, uint256 sharesDue) = IFee(_fee).settle(
_comptrollerProxy,
_vaultProxy,
_hook,
_settlementData,
_gav
);
if (settlementType == SettlementType.None) {
return;
}
address payee;
if (settlementType == SettlementType.Direct) {
payee = IVault(_vaultProxy).getOwner();
__transferShares(_comptrollerProxy, payer, payee, sharesDue);
} elseif (settlementType == SettlementType.Mint) {
payee = IVault(_vaultProxy).getOwner();
__mintShares(_comptrollerProxy, payee, sharesDue);
} elseif (settlementType == SettlementType.Burn) {
__burnShares(_comptrollerProxy, payer, sharesDue);
} elseif (settlementType == SettlementType.MintSharesOutstanding) {
comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] = comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee]
.add(sharesDue);
payee = _vaultProxy;
__mintShares(_comptrollerProxy, payee, sharesDue);
} elseif (settlementType == SettlementType.BurnSharesOutstanding) {
comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee] = comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee]
.sub(sharesDue);
payer = _vaultProxy;
__burnShares(_comptrollerProxy, payer, sharesDue);
} else {
revert("__settleFee: Invalid SettlementType");
}
emit FeeSettledForFund(_comptrollerProxy, _fee, settlementType, payer, payee, sharesDue);
}
/// @dev Helper to settle fees that implement a given fee hookfunction__settleFees(address _comptrollerProxy,
address _vaultProxy,
address[] memory _fees,
FeeHook _hook,
bytesmemory _settlementData,
uint256 _gavOrZero
) privatereturns (uint256 gav_) {
gav_ = _gavOrZero;
for (uint256 i; i < _fees.length; i++) {
if (!feeSettlesOnHook(_fees[i], _hook)) {
continue;
}
gav_ = __getGavAsNecessary(_comptrollerProxy, _fees[i], gav_);
__settleFee(_comptrollerProxy, _vaultProxy, _fees[i], _hook, _settlementData, gav_);
}
return gav_;
}
/// @dev Helper to update fees that implement a given fee hookfunction__updateFees(address _comptrollerProxy,
address _vaultProxy,
address[] memory _fees,
FeeHook _hook,
bytesmemory _settlementData,
uint256 _gavOrZero
) private{
uint256 gav = _gavOrZero;
for (uint256 i; i < _fees.length; i++) {
if (!feeUpdatesOnHook(_fees[i], _hook)) {
continue;
}
gav = __getGavAsNecessary(_comptrollerProxy, _fees[i], gav);
IFee(_fees[i]).update(_comptrollerProxy, _vaultProxy, _hook, _settlementData, gav);
}
}
///////////////////// FEES REGISTRY //////////////////////// @notice Remove fees from the list of registered fees/// @param _fees Addresses of fees to be deregisteredfunctionderegisterFees(address[] calldata _fees) externalonlyFundDeployerOwner{
require(_fees.length>0, "deregisterFees: _fees cannot be empty");
for (uint256 i; i < _fees.length; i++) {
require(isRegisteredFee(_fees[i]), "deregisterFees: fee is not registered");
registeredFees.remove(_fees[i]);
emit FeeDeregistered(_fees[i], IFee(_fees[i]).identifier());
}
}
/// @notice Add fees to the list of registered fees/// @param _fees Addresses of fees to be registered/// @dev Stores the hooks that a fee implements and whether each implementation uses GAV,/// which fronts the gas for calls to check if a hook is implemented, and guarantees/// that these hook implementation return values do not change post-registration.functionregisterFees(address[] calldata _fees) externalonlyFundDeployerOwner{
require(_fees.length>0, "registerFees: _fees cannot be empty");
for (uint256 i; i < _fees.length; i++) {
require(!isRegisteredFee(_fees[i]), "registerFees: fee already registered");
registeredFees.add(_fees[i]);
IFee feeContract = IFee(_fees[i]);
(
FeeHook[] memory implementedHooksForSettle,
FeeHook[] memory implementedHooksForUpdate,
bool usesGavOnSettle,
bool usesGavOnUpdate
) = feeContract.implementedHooks();
// Stores the hooks for which each fee implements settle() and update()for (uint256 j; j < implementedHooksForSettle.length; j++) {
feeToHookToImplementsSettle[_fees[i]][implementedHooksForSettle[j]] =true;
}
for (uint256 j; j < implementedHooksForUpdate.length; j++) {
feeToHookToImplementsUpdate[_fees[i]][implementedHooksForUpdate[j]] =true;
}
// Stores whether each fee requires GAV during its implementations for settle() and update()if (usesGavOnSettle) {
feeToUsesGavOnSettle[_fees[i]] =true;
}
if (usesGavOnUpdate) {
feeToUsesGavOnUpdate[_fees[i]] =true;
}
emit FeeRegistered(
_fees[i],
feeContract.identifier(),
implementedHooksForSettle,
implementedHooksForUpdate,
usesGavOnSettle,
usesGavOnUpdate
);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Get a list of enabled fees for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return enabledFees_ An array of enabled fee addressesfunctiongetEnabledFeesForFund(address _comptrollerProxy)
externalviewreturns (address[] memory enabledFees_)
{
return comptrollerProxyToFees[_comptrollerProxy];
}
/// @notice Get the amount of shares outstanding for a particular fee for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _fee The fee address/// @return sharesOutstanding_ The amount of shares outstandingfunctiongetFeeSharesOutstandingForFund(address _comptrollerProxy, address _fee)
externalviewreturns (uint256 sharesOutstanding_)
{
return comptrollerProxyToFeeToSharesOutstanding[_comptrollerProxy][_fee];
}
/// @notice Get all registered fees/// @return registeredFees_ A list of all registered fee addressesfunctiongetRegisteredFees() externalviewreturns (address[] memory registeredFees_) {
registeredFees_ =newaddress[](registeredFees.length());
for (uint256 i; i < registeredFees_.length; i++) {
registeredFees_[i] = registeredFees.at(i);
}
return registeredFees_;
}
/// @notice Checks if a fee implements settle() on a particular hook/// @param _fee The address of the fee to check/// @param _hook The FeeHook to check/// @return settlesOnHook_ True if the fee settles on the given hookfunctionfeeSettlesOnHook(address _fee, FeeHook _hook)
publicviewreturns (bool settlesOnHook_)
{
return feeToHookToImplementsSettle[_fee][_hook];
}
/// @notice Checks if a fee implements update() on a particular hook/// @param _fee The address of the fee to check/// @param _hook The FeeHook to check/// @return updatesOnHook_ True if the fee updates on the given hookfunctionfeeUpdatesOnHook(address _fee, FeeHook _hook)
publicviewreturns (bool updatesOnHook_)
{
return feeToHookToImplementsUpdate[_fee][_hook];
}
/// @notice Checks if a fee uses GAV in its settle() implementation/// @param _fee The address of the fee to check/// @return usesGav_ True if the fee uses GAV during settle() implementationfunctionfeeUsesGavOnSettle(address _fee) publicviewreturns (bool usesGav_) {
return feeToUsesGavOnSettle[_fee];
}
/// @notice Checks if a fee uses GAV in its update() implementation/// @param _fee The address of the fee to check/// @return usesGav_ True if the fee uses GAV during update() implementationfunctionfeeUsesGavOnUpdate(address _fee) publicviewreturns (bool usesGav_) {
return feeToUsesGavOnUpdate[_fee];
}
/// @notice Check whether a fee is registered/// @param _fee The address of the fee to check/// @return isRegisteredFee_ True if the fee is registeredfunctionisRegisteredFee(address _fee) publicviewreturns (bool isRegisteredFee_) {
return registeredFees.contains(_fee);
}
}
Contract Source Code
File 37 of 138: FundActionsWrapper.sol
// 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.
*/pragmasolidity 0.6.12;import"../core/fund/comptroller/ComptrollerLib.sol";
import"../extensions/fee-manager/FeeManager.sol";
/// @title FundActionsWrapper Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Logic related to funds, not necessary in the core protocol/// @dev This contract is currently unaudited, as it does not involve any/// exchange in value, and simply wraps other functions for conveniencecontractFundActionsWrapper{
addressprivateimmutable FEE_MANAGER;
constructor(address _feeManager) public{
FEE_MANAGER = _feeManager;
}
// EXTERNAL FUNCTIONS/// @notice Calculates the net value of 1 unit of shares in the fund's denomination asset/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return netShareValue_ The amount of the denomination asset per share/// @return isValid_ True if the conversion rates to derive the value are all valid/// @dev Accounts for fees outstanding. This is a convenience function for external consumption/// that can be used to determine the cost of purchasing shares at any given point in time./// It essentially just bundles settling all fees that implement the Continuous hook and then/// looking up the gross share value.functioncalcNetShareValueForFund(address _comptrollerProxy)
externalreturns (uint256 netShareValue_, bool isValid_)
{
ComptrollerLib comptrollerProxyContract = ComptrollerLib(_comptrollerProxy);
comptrollerProxyContract.callOnExtension(FEE_MANAGER, 0, "");
return comptrollerProxyContract.calcGrossShareValue(false);
}
/// @notice Invokes the Continuous fee hook on all specified fees, and then attempts to payout/// any shares outstanding on those fees/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _fees The fees for which to run these actions/// @dev This is just a wrapper to execute two callOnExtension() actions atomically, in sequence./// The caller must pass in the fees that they want to run this logic on.functioninvokeContinuousFeeHookAndPayoutSharesOutstandingForFund(address _comptrollerProxy,
address[] calldata _fees
) external{
ComptrollerLib comptrollerProxyContract = ComptrollerLib(_comptrollerProxy);
comptrollerProxyContract.callOnExtension(FEE_MANAGER, 0, "");
comptrollerProxyContract.callOnExtension(FEE_MANAGER, 1, abi.encode(_fees));
}
// PUBLIC FUNCTIONS/// @notice Gets all fees that implement the `Continuous` fee hook for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return continuousFees_ The fees that implement the `Continuous` fee hookfunctiongetContinuousFeesForFund(address _comptrollerProxy)
publicviewreturns (address[] memory continuousFees_)
{
FeeManager feeManagerContract = FeeManager(FEE_MANAGER);
address[] memory fees = feeManagerContract.getEnabledFeesForFund(_comptrollerProxy);
// Count the continuous feesuint256 continuousFeesCount;
bool[] memory implementsContinuousHook =newbool[](fees.length);
for (uint256 i; i < fees.length; i++) {
if (feeManagerContract.feeSettlesOnHook(fees[i], IFeeManager.FeeHook.Continuous)) {
continuousFeesCount++;
implementsContinuousHook[i] =true;
}
}
// Return early if no continuous feesif (continuousFeesCount ==0) {
returnnewaddress[](0);
}
// Create continuous fees array
continuousFees_ =newaddress[](continuousFeesCount);
uint256 continuousFeesIndex;
for (uint256 i; i < fees.length; i++) {
if (implementsContinuousHook[i]) {
continuousFees_[continuousFeesIndex] = fees[i];
continuousFeesIndex++;
}
}
return continuousFees_;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `FEE_MANAGER` variable/// @return feeManager_ The `FEE_MANAGER` variable valuefunctiongetFeeManager() externalviewreturns (address feeManager_) {
return FEE_MANAGER;
}
}
Contract Source Code
File 38 of 138: FundDeployer.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"../../../persistent/dispatcher/IDispatcher.sol";
import"../../../persistent/utils/IMigrationHookHandler.sol";
import"../fund/comptroller/IComptroller.sol";
import"../fund/comptroller/ComptrollerProxy.sol";
import"../fund/vault/IVault.sol";
import"./IFundDeployer.sol";
/// @title FundDeployer Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice The top-level contract of the release./// It primarily coordinates fund deployment and fund migration, but/// it is also deferred to for contract access control and for allowed calls/// that can be made with a fund's VaultProxy as the msg.sender.contractFundDeployerisIFundDeployer, IMigrationHookHandler{
eventComptrollerLibSet(address comptrollerLib);
eventComptrollerProxyDeployed(addressindexed creator,
address comptrollerProxy,
addressindexed denominationAsset,
uint256 sharesActionTimelock,
bytes feeManagerConfigData,
bytes policyManagerConfigData,
boolindexed forMigration
);
eventNewFundCreated(addressindexed creator,
address comptrollerProxy,
address vaultProxy,
addressindexed fundOwner,
string fundName,
addressindexed denominationAsset,
uint256 sharesActionTimelock,
bytes feeManagerConfigData,
bytes policyManagerConfigData
);
eventReleaseStatusSet(ReleaseStatus indexed prevStatus, ReleaseStatus indexed nextStatus);
eventVaultCallDeregistered(addressindexed contractAddress, bytes4 selector);
eventVaultCallRegistered(addressindexed contractAddress, bytes4 selector);
// Constantsaddressprivateimmutable CREATOR;
addressprivateimmutable DISPATCHER;
addressprivateimmutable VAULT_LIB;
// Pseudo-constants (can only be set once)addressprivate comptrollerLib;
// Storage
ReleaseStatus private releaseStatus;
mapping(address=>mapping(bytes4=>bool)) private contractToSelectorToIsRegisteredVaultCall;
mapping(address=>address) private pendingComptrollerProxyToCreator;
modifieronlyLiveRelease() {
require(releaseStatus == ReleaseStatus.Live, "Release is not Live");
_;
}
modifieronlyMigrator(address _vaultProxy) {
require(
IVault(_vaultProxy).canMigrate(msg.sender),
"Only a permissioned migrator can call this function"
);
_;
}
modifieronlyOwner() {
require(msg.sender== getOwner(), "Only the contract owner can call this function");
_;
}
modifieronlyPendingComptrollerProxyCreator(address _comptrollerProxy) {
require(
msg.sender== pendingComptrollerProxyToCreator[_comptrollerProxy],
"Only the ComptrollerProxy creator can call this function"
);
_;
}
constructor(address _dispatcher,
address _vaultLib,
address[] memory _vaultCallContracts,
bytes4[] memory _vaultCallSelectors
) public{
if (_vaultCallContracts.length>0) {
__registerVaultCalls(_vaultCallContracts, _vaultCallSelectors);
}
CREATOR =msg.sender;
DISPATCHER = _dispatcher;
VAULT_LIB = _vaultLib;
}
/////////////// GENERAL ////////////////// @notice Sets the comptrollerLib/// @param _comptrollerLib The ComptrollerLib contract address/// @dev Can only be set oncefunctionsetComptrollerLib(address _comptrollerLib) externalonlyOwner{
require(
comptrollerLib ==address(0),
"setComptrollerLib: This value can only be set once"
);
comptrollerLib = _comptrollerLib;
emit ComptrollerLibSet(_comptrollerLib);
}
/// @notice Sets the status of the protocol to a new state/// @param _nextStatus The next status state to setfunctionsetReleaseStatus(ReleaseStatus _nextStatus) external{
require(
msg.sender== IDispatcher(DISPATCHER).getOwner(),
"setReleaseStatus: Only the Dispatcher owner can call this function"
);
require(
_nextStatus != ReleaseStatus.PreLaunch,
"setReleaseStatus: Cannot return to PreLaunch status"
);
require(
comptrollerLib !=address(0),
"setReleaseStatus: Can only set the release status when comptrollerLib is set"
);
ReleaseStatus prevStatus = releaseStatus;
require(_nextStatus != prevStatus, "setReleaseStatus: _nextStatus is the current status");
releaseStatus = _nextStatus;
emit ReleaseStatusSet(prevStatus, _nextStatus);
}
/// @notice Gets the current owner of the contract/// @return owner_ The contract owner address/// @dev Dynamically gets the owner based on the Protocol status. The owner is initially the/// contract's deployer, for convenience in setting up configuration./// Ownership is claimed when the owner of the Dispatcher contract (the Enzyme Council)/// sets the releaseStatus to `Live`.functiongetOwner() publicviewoverridereturns (address owner_) {
if (releaseStatus == ReleaseStatus.PreLaunch) {
return CREATOR;
}
return IDispatcher(DISPATCHER).getOwner();
}
///////////////////// FUND CREATION //////////////////////// @notice Creates a fully-configured ComptrollerProxy, to which a fund from a previous/// release can migrate in a subsequent step/// @param _denominationAsset The contract address of the denomination asset for the fund/// @param _sharesActionTimelock The minimum number of seconds between any two "shares actions"/// (buying or selling shares) by the same user/// @param _feeManagerConfigData Bytes data for the fees to be enabled for the fund/// @param _policyManagerConfigData Bytes data for the policies to be enabled for the fund/// @return comptrollerProxy_ The address of the ComptrollerProxy deployed during this actionfunctioncreateMigratedFundConfig(address _denominationAsset,
uint256 _sharesActionTimelock,
bytescalldata _feeManagerConfigData,
bytescalldata _policyManagerConfigData
) externalonlyLiveReleasereturns (address comptrollerProxy_) {
comptrollerProxy_ = __deployComptrollerProxy(
_denominationAsset,
_sharesActionTimelock,
_feeManagerConfigData,
_policyManagerConfigData,
true
);
pendingComptrollerProxyToCreator[comptrollerProxy_] =msg.sender;
return comptrollerProxy_;
}
/// @notice Creates a new fund/// @param _fundOwner The address of the owner for the fund/// @param _fundName The name of the fund/// @param _denominationAsset The contract address of the denomination asset for the fund/// @param _sharesActionTimelock The minimum number of seconds between any two "shares actions"/// (buying or selling shares) by the same user/// @param _feeManagerConfigData Bytes data for the fees to be enabled for the fund/// @param _policyManagerConfigData Bytes data for the policies to be enabled for the fund/// @return comptrollerProxy_ The address of the ComptrollerProxy deployed during this actionfunctioncreateNewFund(address _fundOwner,
stringcalldata _fundName,
address _denominationAsset,
uint256 _sharesActionTimelock,
bytescalldata _feeManagerConfigData,
bytescalldata _policyManagerConfigData
) externalonlyLiveReleasereturns (address comptrollerProxy_, address vaultProxy_) {
return
__createNewFund(
_fundOwner,
_fundName,
_denominationAsset,
_sharesActionTimelock,
_feeManagerConfigData,
_policyManagerConfigData
);
}
/// @dev Helper to avoid the stack-too-deep error during createNewFundfunction__createNewFund(address _fundOwner,
stringmemory _fundName,
address _denominationAsset,
uint256 _sharesActionTimelock,
bytesmemory _feeManagerConfigData,
bytesmemory _policyManagerConfigData
) privatereturns (address comptrollerProxy_, address vaultProxy_) {
require(_fundOwner !=address(0), "__createNewFund: _owner cannot be empty");
comptrollerProxy_ = __deployComptrollerProxy(
_denominationAsset,
_sharesActionTimelock,
_feeManagerConfigData,
_policyManagerConfigData,
false
);
vaultProxy_ = IDispatcher(DISPATCHER).deployVaultProxy(
VAULT_LIB,
_fundOwner,
comptrollerProxy_,
_fundName
);
IComptroller(comptrollerProxy_).activate(vaultProxy_, false);
emit NewFundCreated(
msg.sender,
comptrollerProxy_,
vaultProxy_,
_fundOwner,
_fundName,
_denominationAsset,
_sharesActionTimelock,
_feeManagerConfigData,
_policyManagerConfigData
);
return (comptrollerProxy_, vaultProxy_);
}
/// @dev Helper function to deploy a configured ComptrollerProxyfunction__deployComptrollerProxy(address _denominationAsset,
uint256 _sharesActionTimelock,
bytesmemory _feeManagerConfigData,
bytesmemory _policyManagerConfigData,
bool _forMigration
) privatereturns (address comptrollerProxy_) {
require(
_denominationAsset !=address(0),
"__deployComptrollerProxy: _denominationAsset cannot be empty"
);
bytesmemory constructData =abi.encodeWithSelector(
IComptroller.init.selector,
_denominationAsset,
_sharesActionTimelock
);
comptrollerProxy_ =address(new ComptrollerProxy(constructData, comptrollerLib));
if (_feeManagerConfigData.length>0|| _policyManagerConfigData.length>0) {
IComptroller(comptrollerProxy_).configureExtensions(
_feeManagerConfigData,
_policyManagerConfigData
);
}
emit ComptrollerProxyDeployed(
msg.sender,
comptrollerProxy_,
_denominationAsset,
_sharesActionTimelock,
_feeManagerConfigData,
_policyManagerConfigData,
_forMigration
);
return comptrollerProxy_;
}
//////////////////// MIGRATION IN /////////////////////// @notice Cancels fund migration/// @param _vaultProxy The VaultProxy for which to cancel migrationfunctioncancelMigration(address _vaultProxy) external{
__cancelMigration(_vaultProxy, false);
}
/// @notice Cancels fund migration, bypassing any failures./// Should be used in an emergency only./// @param _vaultProxy The VaultProxy for which to cancel migrationfunctioncancelMigrationEmergency(address _vaultProxy) external{
__cancelMigration(_vaultProxy, true);
}
/// @notice Executes fund migration/// @param _vaultProxy The VaultProxy for which to execute the migrationfunctionexecuteMigration(address _vaultProxy) external{
__executeMigration(_vaultProxy, false);
}
/// @notice Executes fund migration, bypassing any failures./// Should be used in an emergency only./// @param _vaultProxy The VaultProxy for which to execute the migrationfunctionexecuteMigrationEmergency(address _vaultProxy) external{
__executeMigration(_vaultProxy, true);
}
/// @dev UnimplementedfunctioninvokeMigrationInCancelHook(address,
address,
address,
address) externalvirtualoverride{
return;
}
/// @notice Signal a fund migration/// @param _vaultProxy The VaultProxy for which to signal the migration/// @param _comptrollerProxy The ComptrollerProxy for which to signal the migrationfunctionsignalMigration(address _vaultProxy, address _comptrollerProxy) external{
__signalMigration(_vaultProxy, _comptrollerProxy, false);
}
/// @notice Signal a fund migration, bypassing any failures./// Should be used in an emergency only./// @param _vaultProxy The VaultProxy for which to signal the migration/// @param _comptrollerProxy The ComptrollerProxy for which to signal the migrationfunctionsignalMigrationEmergency(address _vaultProxy, address _comptrollerProxy) external{
__signalMigration(_vaultProxy, _comptrollerProxy, true);
}
/// @dev Helper to cancel a migrationfunction__cancelMigration(address _vaultProxy, bool _bypassFailure)
privateonlyLiveReleaseonlyMigrator(_vaultProxy)
{
IDispatcher(DISPATCHER).cancelMigration(_vaultProxy, _bypassFailure);
}
/// @dev Helper to execute a migrationfunction__executeMigration(address _vaultProxy, bool _bypassFailure)
privateonlyLiveReleaseonlyMigrator(_vaultProxy)
{
IDispatcher dispatcherContract = IDispatcher(DISPATCHER);
(, address comptrollerProxy, , ) = dispatcherContract
.getMigrationRequestDetailsForVaultProxy(_vaultProxy);
dispatcherContract.executeMigration(_vaultProxy, _bypassFailure);
IComptroller(comptrollerProxy).activate(_vaultProxy, true);
delete pendingComptrollerProxyToCreator[comptrollerProxy];
}
/// @dev Helper to signal a migrationfunction__signalMigration(address _vaultProxy,
address _comptrollerProxy,
bool _bypassFailure
)
privateonlyLiveReleaseonlyPendingComptrollerProxyCreator(_comptrollerProxy)
onlyMigrator(_vaultProxy)
{
IDispatcher(DISPATCHER).signalMigration(
_vaultProxy,
_comptrollerProxy,
VAULT_LIB,
_bypassFailure
);
}
///////////////////// MIGRATION OUT //////////////////////// @notice Allows "hooking into" specific moments in the migration pipeline/// to execute arbitrary logic during a migration out of this release/// @param _vaultProxy The VaultProxy being migratedfunctioninvokeMigrationOutHook(
MigrationOutHook _hook,
address _vaultProxy,
address,
address,
address) externaloverride{
if (_hook != MigrationOutHook.PreMigrate) {
return;
}
require(
msg.sender== DISPATCHER,
"postMigrateOriginHook: Only Dispatcher can call this function"
);
// Must use PreMigrate hook to get the ComptrollerProxy from the VaultProxyaddress comptrollerProxy = IVault(_vaultProxy).getAccessor();
// Wind down fund and destroy its config
IComptroller(comptrollerProxy).destruct();
}
//////////////// REGISTRY /////////////////// @notice De-registers allowed arbitrary contract calls that can be sent from the VaultProxy/// @param _contracts The contracts of the calls to de-register/// @param _selectors The selectors of the calls to de-registerfunctionderegisterVaultCalls(address[] calldata _contracts, bytes4[] calldata _selectors)
externalonlyOwner{
require(_contracts.length>0, "deregisterVaultCalls: Empty _contracts");
require(
_contracts.length== _selectors.length,
"deregisterVaultCalls: Uneven input arrays"
);
for (uint256 i; i < _contracts.length; i++) {
require(
isRegisteredVaultCall(_contracts[i], _selectors[i]),
"deregisterVaultCalls: Call not registered"
);
contractToSelectorToIsRegisteredVaultCall[_contracts[i]][_selectors[i]] =false;
emit VaultCallDeregistered(_contracts[i], _selectors[i]);
}
}
/// @notice Registers allowed arbitrary contract calls that can be sent from the VaultProxy/// @param _contracts The contracts of the calls to register/// @param _selectors The selectors of the calls to registerfunctionregisterVaultCalls(address[] calldata _contracts, bytes4[] calldata _selectors)
externalonlyOwner{
require(_contracts.length>0, "registerVaultCalls: Empty _contracts");
__registerVaultCalls(_contracts, _selectors);
}
/// @dev Helper to register allowed vault callsfunction__registerVaultCalls(address[] memory _contracts, bytes4[] memory _selectors)
private{
require(
_contracts.length== _selectors.length,
"__registerVaultCalls: Uneven input arrays"
);
for (uint256 i; i < _contracts.length; i++) {
require(
!isRegisteredVaultCall(_contracts[i], _selectors[i]),
"__registerVaultCalls: Call already registered"
);
contractToSelectorToIsRegisteredVaultCall[_contracts[i]][_selectors[i]] =true;
emit VaultCallRegistered(_contracts[i], _selectors[i]);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `comptrollerLib` variable value/// @return comptrollerLib_ The `comptrollerLib` variable valuefunctiongetComptrollerLib() externalviewreturns (address comptrollerLib_) {
return comptrollerLib;
}
/// @notice Gets the `CREATOR` variable value/// @return creator_ The `CREATOR` variable valuefunctiongetCreator() externalviewreturns (address creator_) {
return CREATOR;
}
/// @notice Gets the `DISPATCHER` variable value/// @return dispatcher_ The `DISPATCHER` variable valuefunctiongetDispatcher() externalviewreturns (address dispatcher_) {
return DISPATCHER;
}
/// @notice Gets the creator of a pending ComptrollerProxy/// @return pendingComptrollerProxyCreator_ The pending ComptrollerProxy creatorfunctiongetPendingComptrollerProxyCreator(address _comptrollerProxy)
externalviewreturns (address pendingComptrollerProxyCreator_)
{
return pendingComptrollerProxyToCreator[_comptrollerProxy];
}
/// @notice Gets the `releaseStatus` variable value/// @return status_ The `releaseStatus` variable valuefunctiongetReleaseStatus() externalviewoverridereturns (ReleaseStatus status_) {
return releaseStatus;
}
/// @notice Gets the `VAULT_LIB` variable value/// @return vaultLib_ The `VAULT_LIB` variable valuefunctiongetVaultLib() externalviewreturns (address vaultLib_) {
return VAULT_LIB;
}
/// @notice Checks if a contract call is registered/// @param _contract The contract of the call to check/// @param _selector The selector of the call to check/// @return isRegistered_ True if the call is registeredfunctionisRegisteredVaultCall(address _contract, bytes4 _selector)
publicviewoverridereturns (bool isRegistered_)
{
return contractToSelectorToIsRegisteredVaultCall[_contract][_selector];
}
}
Contract Source Code
File 39 of 138: FundDeployerOwnerMixin.sol
// 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.
*/pragmasolidity 0.6.12;import"../../core/fund-deployer/IFundDeployer.sol";
/// @title FundDeployerOwnerMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract that defers ownership to the owner of FundDeployerabstractcontractFundDeployerOwnerMixin{
addressinternalimmutable FUND_DEPLOYER;
modifieronlyFundDeployerOwner() {
require(
msg.sender== getOwner(),
"onlyFundDeployerOwner: Only the FundDeployer owner can call this function"
);
_;
}
constructor(address _fundDeployer) public{
FUND_DEPLOYER = _fundDeployer;
}
/// @notice Gets the owner of this contract/// @return owner_ The owner/// @dev Ownership is deferred to the owner of the FundDeployer contractfunctiongetOwner() publicviewreturns (address owner_) {
return IFundDeployer(FUND_DEPLOYER).getOwner();
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `FUND_DEPLOYER` variable/// @return fundDeployer_ The `FUND_DEPLOYER` variable valuefunctiongetFundDeployer() externalviewreturns (address fundDeployer_) {
return FUND_DEPLOYER;
}
}
Contract Source Code
File 40 of 138: GuaranteedRedemption.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../utils/FundDeployerOwnerMixin.sol";
import"./utils/PreCallOnIntegrationValidatePolicyBase.sol";
/// @title GuaranteedRedemption Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that guarantees that shares will either be continuously redeemable or/// redeemable within a predictable daily window by preventing trading during a configurable daily periodcontractGuaranteedRedemptionisPreCallOnIntegrationValidatePolicyBase, FundDeployerOwnerMixin{
usingSafeMathforuint256;
eventAdapterAdded(address adapter);
eventAdapterRemoved(address adapter);
eventFundSettingsSet(addressindexed comptrollerProxy,
uint256 startTimestamp,
uint256 duration
);
eventRedemptionWindowBufferSet(uint256 prevBuffer, uint256 nextBuffer);
structRedemptionWindow {
uint256 startTimestamp;
uint256 duration;
}
uint256privateconstant ONE_DAY =24*60*60;
mapping(address=>bool) private adapterToCanBlockRedemption;
mapping(address=> RedemptionWindow) private comptrollerProxyToRedemptionWindow;
uint256private redemptionWindowBuffer;
constructor(address _policyManager,
address _fundDeployer,
uint256 _redemptionWindowBuffer,
address[] memory _redemptionBlockingAdapters
) publicPolicyBase(_policyManager) FundDeployerOwnerMixin(_fundDeployer) {
redemptionWindowBuffer = _redemptionWindowBuffer;
__addRedemptionBlockingAdapters(_redemptionBlockingAdapters);
}
// EXTERNAL FUNCTIONS/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
(uint256 startTimestamp, uint256 duration) =abi.decode(
_encodedSettings,
(uint256, uint256)
);
if (startTimestamp ==0) {
require(duration ==0, "addFundSettings: duration must be 0 if startTimestamp is 0");
return;
}
// Use 23 hours instead of 1 day to allow up to 1 hr of redemptionWindowBufferrequire(
duration >0&& duration <=23hours,
"addFundSettings: duration must be between 1 second and 23 hours"
);
comptrollerProxyToRedemptionWindow[_comptrollerProxy].startTimestamp = startTimestamp;
comptrollerProxyToRedemptionWindow[_comptrollerProxy].duration = duration;
emit FundSettingsSet(_comptrollerProxy, startTimestamp, duration);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"GUARANTEED_REDEMPTION";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _adapter The adapter for which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address _adapter)
publicviewreturns (bool isValid_)
{
if (!adapterCanBlockRedemption(_adapter)) {
returntrue;
}
RedemptionWindow memory redemptionWindow
= comptrollerProxyToRedemptionWindow[_comptrollerProxy];
// If no RedemptionWindow is set, the fund can never use redemption-blocking adaptersif (redemptionWindow.startTimestamp ==0) {
returnfalse;
}
uint256 latestRedemptionWindowStart = calcLatestRedemptionWindowStart(
redemptionWindow.startTimestamp
);
// A fund can't trade during its redemption window, nor in the buffer beforehand.// The lower bound is only relevant when the startTimestamp is in the future,// so we check it last.if (
block.timestamp>= latestRedemptionWindowStart.add(redemptionWindow.duration) ||block.timestamp<= latestRedemptionWindowStart.sub(redemptionWindowBuffer)
) {
returntrue;
}
returnfalse;
}
/// @notice Sets a new value for the redemptionWindowBuffer variable/// @param _nextRedemptionWindowBuffer The number of seconds for the redemptionWindowBuffer/// @dev The redemptionWindowBuffer is added to the beginning of the redemption window,/// and should always be >= the longest potential block on redemption amongst all adapters./// (e.g., Synthetix blocks token transfers during a timelock after trading synths)functionsetRedemptionWindowBuffer(uint256 _nextRedemptionWindowBuffer)
externalonlyFundDeployerOwner{
uint256 prevRedemptionWindowBuffer = redemptionWindowBuffer;
require(
_nextRedemptionWindowBuffer != prevRedemptionWindowBuffer,
"setRedemptionWindowBuffer: Value already set"
);
redemptionWindowBuffer = _nextRedemptionWindowBuffer;
emit RedemptionWindowBufferSet(prevRedemptionWindowBuffer, _nextRedemptionWindowBuffer);
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(address adapter, ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, adapter);
}
// PUBLIC FUNCTIONS/// @notice Calculates the start of the most recent redemption window/// @param _startTimestamp The initial startTimestamp for the redemption window/// @return latestRedemptionWindowStart_ The starting timestamp of the most recent redemption windowfunctioncalcLatestRedemptionWindowStart(uint256 _startTimestamp)
publicviewreturns (uint256 latestRedemptionWindowStart_)
{
if (block.timestamp<= _startTimestamp) {
return _startTimestamp;
}
uint256 timeSinceStartTimestamp =block.timestamp.sub(_startTimestamp);
uint256 timeSincePeriodStart = timeSinceStartTimestamp.mod(ONE_DAY);
returnblock.timestamp.sub(timeSincePeriodStart);
}
///////////////////////////////////////////// REDEMPTION-BLOCKING ADAPTERS REGISTRY //////////////////////////////////////////////// @notice Add adapters which can block shares redemption/// @param _adapters The addresses of adapters to be addedfunctionaddRedemptionBlockingAdapters(address[] calldata _adapters)
externalonlyFundDeployerOwner{
require(
_adapters.length>0,
"__addRedemptionBlockingAdapters: _adapters cannot be empty"
);
__addRedemptionBlockingAdapters(_adapters);
}
/// @notice Remove adapters which can block shares redemption/// @param _adapters The addresses of adapters to be removedfunctionremoveRedemptionBlockingAdapters(address[] calldata _adapters)
externalonlyFundDeployerOwner{
require(
_adapters.length>0,
"removeRedemptionBlockingAdapters: _adapters cannot be empty"
);
for (uint256 i; i < _adapters.length; i++) {
require(
adapterCanBlockRedemption(_adapters[i]),
"removeRedemptionBlockingAdapters: adapter is not added"
);
adapterToCanBlockRedemption[_adapters[i]] =false;
emit AdapterRemoved(_adapters[i]);
}
}
/// @dev Helper to mark adapters that can block shares redemptionfunction__addRedemptionBlockingAdapters(address[] memory _adapters) private{
for (uint256 i; i < _adapters.length; i++) {
require(
_adapters[i] !=address(0),
"__addRedemptionBlockingAdapters: adapter cannot be empty"
);
require(
!adapterCanBlockRedemption(_adapters[i]),
"__addRedemptionBlockingAdapters: adapter already added"
);
adapterToCanBlockRedemption[_adapters[i]] =true;
emit AdapterAdded(_adapters[i]);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `redemptionWindowBuffer` variable/// @return redemptionWindowBuffer_ The `redemptionWindowBuffer` variable valuefunctiongetRedemptionWindowBuffer() externalviewreturns (uint256 redemptionWindowBuffer_) {
return redemptionWindowBuffer;
}
/// @notice Gets the RedemptionWindow settings for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return redemptionWindow_ The RedemptionWindow settingsfunctiongetRedemptionWindowForFund(address _comptrollerProxy)
externalviewreturns (RedemptionWindow memory redemptionWindow_)
{
return comptrollerProxyToRedemptionWindow[_comptrollerProxy];
}
/// @notice Checks whether an adapter can block shares redemption/// @param _adapter The address of the adapter to check/// @return canBlockRedemption_ True if the adapter can block shares redemptionfunctionadapterCanBlockRedemption(address _adapter)
publicviewreturns (bool canBlockRedemption_)
{
return adapterToCanBlockRedemption[_adapter];
}
}
Contract Source Code
File 41 of 138: IAggregatedDerivativePriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"./IDerivativePriceFeed.sol";
/// @title IDerivativePriceFeed Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIAggregatedDerivativePriceFeedisIDerivativePriceFeed{
functiongetPriceFeedForDerivative(address) externalviewreturns (address);
}
Contract Source Code
File 42 of 138: IAuthUserExecutedSharesRequestor.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IAuthUserExecutedSharesRequestor Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIAuthUserExecutedSharesRequestor{
functioninit(address) external;
}
Contract Source Code
File 43 of 138: ICERC20.sol
// 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.
*/pragmasolidity ^0.6.12;import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title ICERC20 Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Minimal interface for interactions with Compound tokens (cTokens)interfaceICERC20isIERC20{
functiondecimals() externalviewreturns (uint8);
functionmint(uint256) externalreturns (uint256);
functionredeem(uint256) externalreturns (uint256);
functionexchangeRateStored() externalviewreturns (uint256);
functionunderlying() externalreturns (address);
}
Contract Source Code
File 44 of 138: ICEther.sol
// 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.
*/pragmasolidity ^0.6.12;/// @title ICEther Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Minimal interface for interactions with Compound EtherinterfaceICEther{
functionmint() externalpayable;
}
Contract Source Code
File 45 of 138: IChai.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title IChai Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Minimal interface for our interactions with the Chai contractinterfaceIChaiisIERC20{
functionexit(address, uint256) external;
functionjoin(address, uint256) external;
}
Contract Source Code
File 46 of 138: IChainlinkAggregator.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IChainlinkAggregator Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIChainlinkAggregator{
functionlatestAnswer() externalviewreturns (int256);
functionlatestTimestamp() externalviewreturns (uint256);
}
Contract Source Code
File 47 of 138: IComptroller.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IComptroller Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIComptroller{
enumVaultAction {
None,
BurnShares,
MintShares,
TransferShares,
ApproveAssetSpender,
WithdrawAssetTo,
AddTrackedAsset,
RemoveTrackedAsset
}
functionactivate(address, bool) external;
functioncalcGav(bool) externalreturns (uint256, bool);
functioncalcGrossShareValue(bool) externalreturns (uint256, bool);
functioncallOnExtension(address,
uint256,
bytescalldata) external;
functionconfigureExtensions(bytescalldata, bytescalldata) external;
functiondestruct() external;
functiongetDenominationAsset() externalviewreturns (address);
functiongetVaultProxy() externalviewreturns (address);
functioninit(address, uint256) external;
functionpermissionedVaultAction(VaultAction, bytescalldata) external;
}
Contract Source Code
File 48 of 138: IDerivativePriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IDerivativePriceFeed Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Simple interface for derivative price source oracle implementationsinterfaceIDerivativePriceFeed{
functioncalcUnderlyingValues(address, uint256)
externalreturns (address[] memory, uint256[] memory);
functionisSupportedAsset(address) externalviewreturns (bool);
}
Contract Source Code
File 49 of 138: IDispatcher.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IDispatcher Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIDispatcher{
functioncancelMigration(address _vaultProxy, bool _bypassFailure) external;
functionclaimOwnership() external;
functiondeployVaultProxy(address _vaultLib,
address _owner,
address _vaultAccessor,
stringcalldata _fundName
) externalreturns (address vaultProxy_);
functionexecuteMigration(address _vaultProxy, bool _bypassFailure) external;
functiongetCurrentFundDeployer() externalviewreturns (address currentFundDeployer_);
functiongetFundDeployerForVaultProxy(address _vaultProxy)
externalviewreturns (address fundDeployer_);
functiongetMigrationRequestDetailsForVaultProxy(address _vaultProxy)
externalviewreturns (address nextFundDeployer_,
address nextVaultAccessor_,
address nextVaultLib_,
uint256 executableTimestamp_
);
functiongetMigrationTimelock() externalviewreturns (uint256 migrationTimelock_);
functiongetNominatedOwner() externalviewreturns (address nominatedOwner_);
functiongetOwner() externalviewreturns (address owner_);
functiongetSharesTokenSymbol() externalviewreturns (stringmemory sharesTokenSymbol_);
functiongetTimelockRemainingForMigrationRequest(address _vaultProxy)
externalviewreturns (uint256 secondsRemaining_);
functionhasExecutableMigrationRequest(address _vaultProxy)
externalviewreturns (bool hasExecutableRequest_);
functionhasMigrationRequest(address _vaultProxy)
externalviewreturns (bool hasMigrationRequest_);
functionremoveNominatedOwner() external;
functionsetCurrentFundDeployer(address _nextFundDeployer) external;
functionsetMigrationTimelock(uint256 _nextTimelock) external;
functionsetNominatedOwner(address _nextNominatedOwner) external;
functionsetSharesTokenSymbol(stringcalldata _nextSymbol) external;
functionsignalMigration(address _vaultProxy,
address _nextVaultAccessor,
address _nextVaultLib,
bool _bypassFailure
) external;
}
Contract Source Code
File 50 of 138: IERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 51 of 138: IExtension.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IExtension Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for all extensionsinterfaceIExtension{
functionactivateForFund(bool _isMigration) external;
functiondeactivateForFund() external;
functionreceiveCallFromComptroller(address _comptrollerProxy,
uint256 _actionId,
bytescalldata _callArgs
) external;
functionsetConfigForFund(bytescalldata _configData) external;
}
Contract Source Code
File 52 of 138: IFee.sol
// 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.
*/pragmasolidity 0.6.12;import"./IFeeManager.sol";
/// @title Fee Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for all feesinterfaceIFee{
functionactivateForFund(address _comptrollerProxy, address _vaultProxy) external;
functionaddFundSettings(address _comptrollerProxy, bytescalldata _settingsData) external;
functionidentifier() externalpurereturns (stringmemory identifier_);
functionimplementedHooks()
externalviewreturns (
IFeeManager.FeeHook[] memory implementedHooksForSettle_,
IFeeManager.FeeHook[] memory implementedHooksForUpdate_,
bool usesGavOnSettle_,
bool usesGavOnUpdate_
);
functionpayout(address _comptrollerProxy, address _vaultProxy)
externalreturns (bool isPayable_);
functionsettle(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook _hook,
bytescalldata _settlementData,
uint256 _gav
)
externalreturns (
IFeeManager.SettlementType settlementType_,
address payer_,
uint256 sharesDue_
);
functionupdate(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook _hook,
bytescalldata _settlementData,
uint256 _gav
) external;
}
Contract Source Code
File 53 of 138: IFeeManager.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;/// @title FeeManager Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for the FeeManagerinterfaceIFeeManager{
// No fees for the current release are implemented post-redeemSharesenumFeeHook {
Continuous,
BuySharesSetup,
PreBuyShares,
PostBuyShares,
BuySharesCompleted,
PreRedeemShares
}
enumSettlementType {None, Direct, Mint, Burn, MintSharesOutstanding, BurnSharesOutstanding}
functioninvokeHook(
FeeHook,
bytescalldata,
uint256) external;
}
Contract Source Code
File 54 of 138: IFundDeployer.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IFundDeployer Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIFundDeployer{
enumReleaseStatus {PreLaunch, Live, Paused}
functiongetOwner() externalviewreturns (address);
functiongetReleaseStatus() externalviewreturns (ReleaseStatus);
functionisRegisteredVaultCall(address, bytes4) externalviewreturns (bool);
}
Contract Source Code
File 55 of 138: IIntegrationAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"../IIntegrationManager.sol";
/// @title Integration Adapter interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for all integration adaptersinterfaceIIntegrationAdapter{
functionidentifier() externalpurereturns (stringmemory identifier_);
functionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewreturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
);
}
Contract Source Code
File 56 of 138: IIntegrationManager.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IIntegrationManager interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for the IntegrationManagerinterfaceIIntegrationManager{
enumSpendAssetsHandleType {None, Approve, Transfer, Remove}
}
Contract Source Code
File 57 of 138: IKyberNetworkProxy.sol
// 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.
*/pragmasolidity 0.6.12;/// @title Kyber Network interfaceinterfaceIKyberNetworkProxy{
functionswapEtherToToken(address, uint256) externalpayablereturns (uint256);
functionswapTokenToEther(address,
uint256,
uint256) externalreturns (uint256);
functionswapTokenToToken(address,
uint256,
address,
uint256) externalreturns (uint256);
}
Contract Source Code
File 58 of 138: IMakerDaoPot.sol
// 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.
*/pragmasolidity 0.6.12;/// @notice Limited interface for Maker DSR's Pot contract/// @dev See DSR integration guide: https://github.com/makerdao/developerguides/blob/master/dai/dsr-integration-guide/dsr-integration-guide-01.mdinterfaceIMakerDaoPot{
functionchi() externalviewreturns (uint256);
functionrho() externalviewreturns (uint256);
functiondrip() externalreturns (uint256);
}
Contract Source Code
File 59 of 138: IMigratableVault.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IMigratableVault Interface/// @author Enzyme Council <security@enzyme.finance>/// @dev DO NOT EDIT CONTRACTinterfaceIMigratableVault{
functioncanMigrate(address _who) externalviewreturns (bool canMigrate_);
functioninit(address _owner,
address _accessor,
stringcalldata _fundName
) external;
functionsetAccessor(address _nextAccessor) external;
functionsetVaultLib(address _nextVaultLib) external;
}
Contract Source Code
File 60 of 138: IMigrationHookHandler.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IMigrationHookHandler Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIMigrationHookHandler{
enumMigrationOutHook {PreSignal, PostSignal, PreMigrate, PostMigrate, PostCancel}
functioninvokeMigrationInCancelHook(address _vaultProxy,
address _prevFundDeployer,
address _nextVaultAccessor,
address _nextVaultLib
) external;
functioninvokeMigrationOutHook(
MigrationOutHook _hook,
address _vaultProxy,
address _nextFundDeployer,
address _nextVaultAccessor,
address _nextVaultLib
) external;
}
Contract Source Code
File 61 of 138: IParaSwapAugustusSwapper.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;/// @title ParaSwap IAugustusSwapper interfaceinterfaceIParaSwapAugustusSwapper{
structRoute {
addresspayable exchange;
address targetExchange;
uint256 percent;
bytes payload;
uint256 networkFee;
}
structPath {
address to;
uint256 totalNetworkFee;
Route[] routes;
}
functionmultiSwap(address,
address,
uint256,
uint256,
uint256,
Path[] calldata,
uint256,
addresspayable,
uint256,
stringcalldata) externalpayablereturns (uint256);
}
Contract Source Code
File 62 of 138: IPolicy.sol
// 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.
*/pragmasolidity 0.6.12;import"./IPolicyManager.sol";
/// @title Policy Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIPolicy{
functionactivateForFund(address _comptrollerProxy, address _vaultProxy) external;
functionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings) external;
functionidentifier() externalpurereturns (stringmemory identifier_);
functionimplementedHooks()
externalviewreturns (IPolicyManager.PolicyHook[] memory implementedHooks_);
functionupdateFundSettings(address _comptrollerProxy,
address _vaultProxy,
bytescalldata _encodedSettings
) external;
functionvalidateRule(address _comptrollerProxy,
address _vaultProxy,
IPolicyManager.PolicyHook _hook,
bytescalldata _encodedArgs
) externalreturns (bool isValid_);
}
Contract Source Code
File 63 of 138: IPolicyManager.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;/// @title PolicyManager Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for the PolicyManagerinterfaceIPolicyManager{
enumPolicyHook {
BuySharesSetup,
PreBuyShares,
PostBuyShares,
BuySharesCompleted,
PreCallOnIntegration,
PostCallOnIntegration
}
functionvalidatePolicies(address,
PolicyHook,
bytescalldata) external;
}
Contract Source Code
File 64 of 138: IPrimitivePriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IPrimitivePriceFeed Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for primitive price feedsinterfaceIPrimitivePriceFeed{
functioncalcCanonicalValue(address,
uint256,
address) externalviewreturns (uint256, bool);
functioncalcLiveValue(address,
uint256,
address) externalviewreturns (uint256, bool);
functionisSupportedAsset(address) externalviewreturns (bool);
}
Contract Source Code
File 65 of 138: ISynthetix.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetix Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetix{
functionexchangeOnBehalfWithTracking(address,
bytes32,
uint256,
bytes32,
address,
bytes32) externalreturns (uint256);
}
Contract Source Code
File 66 of 138: ISynthetixAddressResolver.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetixAddressResolver Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetixAddressResolver{
functionrequireAndGetAddress(bytes32, stringcalldata) externalviewreturns (address);
}
Contract Source Code
File 67 of 138: ISynthetixExchangeRates.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetixExchangeRates Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetixExchangeRates{
functionrateAndInvalid(bytes32) externalviewreturns (uint256, bool);
}
Contract Source Code
File 68 of 138: ISynthetixExchanger.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetixExchanger Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetixExchanger{
functiongetAmountsForExchange(uint256,
bytes32,
bytes32)
externalviewreturns (uint256,
uint256,
uint256);
functionsettle(address, bytes32)
externalreturns (uint256,
uint256,
uint256);
}
Contract Source Code
File 69 of 138: ISynthetixProxyERC20.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetixProxyERC20 Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetixProxyERC20{
functiontarget() externalviewreturns (address);
}
Contract Source Code
File 70 of 138: ISynthetixSynth.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ISynthetixSynth Interface/// @author Enzyme Council <security@enzyme.finance>interfaceISynthetixSynth{
functioncurrencyKey() externalviewreturns (bytes32);
}
Contract Source Code
File 71 of 138: IUniswapV2Factory.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IUniswapV2Factory Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Minimal interface for our interactions with the Uniswap V2's Factory contractinterfaceIUniswapV2Factory{
functionfeeTo() externalviewreturns (address);
functiongetPair(address, address) externalviewreturns (address);
}
Contract Source Code
File 72 of 138: IUniswapV2Pair.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IUniswapV2Pair Interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Minimal interface for our interactions with the Uniswap V2's Pair contractinterfaceIUniswapV2Pair{
functiongetReserves()
externalviewreturns (uint112,
uint112,
uint32);
functionkLast() externalviewreturns (uint256);
functiontoken0() externalviewreturns (address);
functiontoken1() externalviewreturns (address);
functiontotalSupply() externalviewreturns (uint256);
}
Contract Source Code
File 73 of 138: IUniswapV2Router2.sol
// 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.
*/pragmasolidity 0.6.12;/// @title UniswapV2Router2 Interface/// @author Enzyme Council <security@enzyme.finance>/// @dev Minimal interface for our interactions with Uniswap V2's Router2interfaceIUniswapV2Router2{
functionaddLiquidity(address,
address,
uint256,
uint256,
uint256,
uint256,
address,
uint256)
externalreturns (uint256,
uint256,
uint256);
functionremoveLiquidity(address,
address,
uint256,
uint256,
uint256,
address,
uint256) externalreturns (uint256, uint256);
functionswapExactTokensForTokens(uint256,
uint256,
address[] calldata,
address,
uint256) externalreturns (uint256[] memory);
}
Contract Source Code
File 74 of 138: IValueInterpreter.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IValueInterpreter interface/// @author Enzyme Council <security@enzyme.finance>/// @notice Interface for ValueInterpreterinterfaceIValueInterpreter{
functioncalcCanonicalAssetValue(address,
uint256,
address) externalreturns (uint256, bool);
functioncalcCanonicalAssetsTotalValue(address[] calldata,
uint256[] calldata,
address) externalreturns (uint256, bool);
functioncalcLiveAssetValue(address,
uint256,
address) externalreturns (uint256, bool);
functioncalcLiveAssetsTotalValue(address[] calldata,
uint256[] calldata,
address) externalreturns (uint256, bool);
}
Contract Source Code
File 75 of 138: IVault.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../persistent/utils/IMigratableVault.sol";
/// @title IVault Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIVaultisIMigratableVault{
functionaddTrackedAsset(address) external;
functionapproveAssetSpender(address,
address,
uint256) external;
functionburnShares(address, uint256) external;
functioncallOnContract(address, bytescalldata) external;
functiongetAccessor() externalviewreturns (address);
functiongetOwner() externalviewreturns (address);
functiongetTrackedAssets() externalviewreturns (address[] memory);
functionisTrackedAsset(address) externalviewreturns (bool);
functionmintShares(address, uint256) external;
functionremoveTrackedAsset(address) external;
functiontransferShares(address,
address,
uint256) external;
functionwithdrawAssetTo(address,
address,
uint256) external;
}
Contract Source Code
File 76 of 138: IWETH.sol
// 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.
*/pragmasolidity 0.6.12;/// @title WETH Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIWETH{
functiondeposit() externalpayable;
functionwithdraw(uint256) external;
}
Contract Source Code
File 77 of 138: IZeroExV2.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;/// @dev Minimal interface for our interactions with the ZeroEx Exchange contractinterfaceIZeroExV2{
structOrder {
address makerAddress;
address takerAddress;
address feeRecipientAddress;
address senderAddress;
uint256 makerAssetAmount;
uint256 takerAssetAmount;
uint256 makerFee;
uint256 takerFee;
uint256 expirationTimeSeconds;
uint256 salt;
bytes makerAssetData;
bytes takerAssetData;
}
structOrderInfo {
uint8 orderStatus;
bytes32 orderHash;
uint256 orderTakerAssetFilledAmount;
}
structFillResults {
uint256 makerAssetFilledAmount;
uint256 takerAssetFilledAmount;
uint256 makerFeePaid;
uint256 takerFeePaid;
}
functionZRX_ASSET_DATA() externalviewreturns (bytesmemory);
functionfilled(bytes32) externalviewreturns (uint256);
functioncancelled(bytes32) externalviewreturns (bool);
functiongetOrderInfo(Order calldata) externalviewreturns (OrderInfo memory);
functiongetAssetProxy(bytes4) externalviewreturns (address);
functionisValidSignature(bytes32,
address,
bytescalldata) externalviewreturns (bool);
functionpreSign(bytes32,
address,
bytescalldata) external;
functioncancelOrder(Order calldata) external;
functionfillOrder(
Order calldata,
uint256,
bytescalldata) externalreturns (FillResults memory);
}
Contract Source Code
File 78 of 138: IntegrationManager.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/utils/EnumerableSet.sol";
import"../../core/fund/vault/IVault.sol";
import"../../infrastructure/price-feeds/derivatives/IDerivativePriceFeed.sol";
import"../../infrastructure/price-feeds/primitives/IPrimitivePriceFeed.sol";
import"../../utils/AddressArrayLib.sol";
import"../../utils/AssetFinalityResolver.sol";
import"../policy-manager/IPolicyManager.sol";
import"../utils/ExtensionBase.sol";
import"../utils/FundDeployerOwnerMixin.sol";
import"../utils/PermissionedVaultActionMixin.sol";
import"./integrations/IIntegrationAdapter.sol";
import"./IIntegrationManager.sol";
/// @title IntegrationManager/// @author Enzyme Council <security@enzyme.finance>/// @notice Extension to handle DeFi integration actions for fundscontractIntegrationManagerisIIntegrationManager,
ExtensionBase,
FundDeployerOwnerMixin,
PermissionedVaultActionMixin,
AssetFinalityResolver{
usingAddressArrayLibforaddress[];
usingEnumerableSetforEnumerableSet.AddressSet;
usingSafeMathforuint256;
eventAdapterDeregistered(addressindexed adapter, stringindexed identifier);
eventAdapterRegistered(addressindexed adapter, stringindexed identifier);
eventAuthUserAddedForFund(addressindexed comptrollerProxy, addressindexed account);
eventAuthUserRemovedForFund(addressindexed comptrollerProxy, addressindexed account);
eventCallOnIntegrationExecutedForFund(addressindexed comptrollerProxy,
address vaultProxy,
address caller,
addressindexed adapter,
bytes4indexed selector,
bytes integrationData,
address[] incomingAssets,
uint256[] incomingAssetAmounts,
address[] outgoingAssets,
uint256[] outgoingAssetAmounts
);
addressprivateimmutable DERIVATIVE_PRICE_FEED;
addressprivateimmutable POLICY_MANAGER;
addressprivateimmutable PRIMITIVE_PRICE_FEED;
EnumerableSet.AddressSet private registeredAdapters;
mapping(address=>mapping(address=>bool)) private comptrollerProxyToAcctToIsAuthUser;
constructor(address _fundDeployer,
address _policyManager,
address _derivativePriceFeed,
address _primitivePriceFeed,
address _synthetixPriceFeed,
address _synthetixAddressResolver
)
publicFundDeployerOwnerMixin(_fundDeployer)
AssetFinalityResolver(_synthetixPriceFeed, _synthetixAddressResolver)
{
DERIVATIVE_PRICE_FEED = _derivativePriceFeed;
POLICY_MANAGER = _policyManager;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
}
/////////////// GENERAL ////////////////// @notice Activates the extension by storing the VaultProxyfunctionactivateForFund(bool) externaloverride{
__setValidatedVaultProxy(msg.sender);
}
/// @notice Authorizes a user to act on behalf of a fund via the IntegrationManager/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _who The user to authorizefunctionaddAuthUserForFund(address _comptrollerProxy, address _who) external{
__validateSetAuthUser(_comptrollerProxy, _who, true);
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who] =true;
emit AuthUserAddedForFund(_comptrollerProxy, _who);
}
/// @notice Deactivate the extension by destroying storagefunctiondeactivateForFund() externaloverride{
delete comptrollerProxyToVaultProxy[msg.sender];
}
/// @notice Removes an authorized user from the IntegrationManager for the given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _who The authorized user to removefunctionremoveAuthUserForFund(address _comptrollerProxy, address _who) external{
__validateSetAuthUser(_comptrollerProxy, _who, false);
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who] =false;
emit AuthUserRemovedForFund(_comptrollerProxy, _who);
}
/// @notice Checks whether an account is an authorized IntegrationManager user for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _who The account to check/// @return isAuthUser_ True if the account is an authorized user or the fund ownerfunctionisAuthUserForFund(address _comptrollerProxy, address _who)
publicviewreturns (bool isAuthUser_)
{
return
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who] ||
_who == IVault(comptrollerProxyToVaultProxy[_comptrollerProxy]).getOwner();
}
/// @dev Helper to validate calls to update comptrollerProxyToAcctToIsAuthUserfunction__validateSetAuthUser(address _comptrollerProxy,
address _who,
bool _nextIsAuthUser
) privateview{
require(
comptrollerProxyToVaultProxy[_comptrollerProxy] !=address(0),
"__validateSetAuthUser: Fund has not been activated"
);
address fundOwner = IVault(comptrollerProxyToVaultProxy[_comptrollerProxy]).getOwner();
require(
msg.sender== fundOwner,
"__validateSetAuthUser: Only the fund owner can call this function"
);
require(_who != fundOwner, "__validateSetAuthUser: Cannot set for the fund owner");
if (_nextIsAuthUser) {
require(
!comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who],
"__validateSetAuthUser: Account is already an authorized user"
);
} else {
require(
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who],
"__validateSetAuthUser: Account is not an authorized user"
);
}
}
///////////////////////////////// CALL-ON-EXTENSION ACTIONS //////////////////////////////////// @notice Receives a dispatched `callOnExtension` from a fund's ComptrollerProxy/// @param _caller The user who called for this action/// @param _actionId An ID representing the desired action/// @param _callArgs The encoded args for the actionfunctionreceiveCallFromComptroller(address _caller,
uint256 _actionId,
bytescalldata _callArgs
) externaloverride{
// Since we validate and store the ComptrollerProxy-VaultProxy pairing during// activateForFund(), this function does not require further validation of the// sending ComptrollerProxyaddress vaultProxy = comptrollerProxyToVaultProxy[msg.sender];
require(vaultProxy !=address(0), "receiveCallFromComptroller: Fund is not active");
require(
isAuthUserForFund(msg.sender, _caller),
"receiveCallFromComptroller: Not an authorized user"
);
// Dispatch the actionif (_actionId ==0) {
__callOnIntegration(_caller, vaultProxy, _callArgs);
} elseif (_actionId ==1) {
__addZeroBalanceTrackedAssets(vaultProxy, _callArgs);
} elseif (_actionId ==2) {
__removeZeroBalanceTrackedAssets(vaultProxy, _callArgs);
} else {
revert("receiveCallFromComptroller: Invalid _actionId");
}
}
/// @dev Adds assets with a zero balance as tracked assets of the fundfunction__addZeroBalanceTrackedAssets(address _vaultProxy, bytesmemory _callArgs) private{
address[] memory assets =abi.decode(_callArgs, (address[]));
for (uint256 i; i < assets.length; i++) {
require(
__finalizeIfSynthAndGetAssetBalance(_vaultProxy, assets[i], true) ==0,
"__addZeroBalanceTrackedAssets: Balance is not zero"
);
__addTrackedAsset(msg.sender, assets[i]);
}
}
/// @dev Removes assets with a zero balance from tracked assets of the fundfunction__removeZeroBalanceTrackedAssets(address _vaultProxy, bytesmemory _callArgs)
private{
address[] memory assets =abi.decode(_callArgs, (address[]));
address denominationAsset = IComptroller(msg.sender).getDenominationAsset();
for (uint256 i; i < assets.length; i++) {
require(
assets[i] != denominationAsset,
"__removeZeroBalanceTrackedAssets: Cannot remove denomination asset"
);
require(
__finalizeIfSynthAndGetAssetBalance(_vaultProxy, assets[i], true) ==0,
"__removeZeroBalanceTrackedAssets: Balance is not zero"
);
__removeTrackedAsset(msg.sender, assets[i]);
}
}
/////////////////////////// CALL ON INTEGRATION ////////////////////////////// @notice Universal method for calling third party contract functions through adapters/// @param _caller The caller of this function via the ComptrollerProxy/// @param _vaultProxy The VaultProxy of the fund/// @param _callArgs The encoded args for this function/// - _adapter Adapter of the integration on which to execute a call/// - _selector Method selector of the adapter method to execute/// - _integrationData Encoded arguments specific to the adapter/// @dev msg.sender is the ComptrollerProxy./// Refer to specific adapter to see how to encode its arguments.function__callOnIntegration(address _caller,
address _vaultProxy,
bytesmemory _callArgs
) private{
(
address adapter,
bytes4 selector,
bytesmemory integrationData
) = __decodeCallOnIntegrationArgs(_callArgs);
__preCoIHook(adapter, selector);
/// Passing decoded _callArgs leads to stack-too-deep error
(
address[] memory incomingAssets,
uint256[] memory incomingAssetAmounts,
address[] memory outgoingAssets,
uint256[] memory outgoingAssetAmounts
) = __callOnIntegrationInner(_vaultProxy, _callArgs);
__postCoIHook(
adapter,
selector,
incomingAssets,
incomingAssetAmounts,
outgoingAssets,
outgoingAssetAmounts
);
__emitCoIEvent(
_vaultProxy,
_caller,
adapter,
selector,
integrationData,
incomingAssets,
incomingAssetAmounts,
outgoingAssets,
outgoingAssetAmounts
);
}
/// @dev Helper to execute the bulk of logic of callOnIntegration./// Avoids the stack-too-deep-error.function__callOnIntegrationInner(address vaultProxy, bytesmemory _callArgs)
privatereturns (address[] memory incomingAssets_,
uint256[] memory incomingAssetAmounts_,
address[] memory outgoingAssets_,
uint256[] memory outgoingAssetAmounts_
)
{
(
address[] memory expectedIncomingAssets,
uint256[] memory preCallIncomingAssetBalances,
uint256[] memory minIncomingAssetAmounts,
SpendAssetsHandleType spendAssetsHandleType,
address[] memory spendAssets,
uint256[] memory maxSpendAssetAmounts,
uint256[] memory preCallSpendAssetBalances
) = __preProcessCoI(vaultProxy, _callArgs);
__executeCoI(
vaultProxy,
_callArgs,
abi.encode(
spendAssetsHandleType,
spendAssets,
maxSpendAssetAmounts,
expectedIncomingAssets
)
);
(
incomingAssets_,
incomingAssetAmounts_,
outgoingAssets_,
outgoingAssetAmounts_
) = __postProcessCoI(
vaultProxy,
expectedIncomingAssets,
preCallIncomingAssetBalances,
minIncomingAssetAmounts,
spendAssetsHandleType,
spendAssets,
maxSpendAssetAmounts,
preCallSpendAssetBalances
);
return (incomingAssets_, incomingAssetAmounts_, outgoingAssets_, outgoingAssetAmounts_);
}
/// @dev Helper to decode CoI argsfunction__decodeCallOnIntegrationArgs(bytesmemory _callArgs)
privatepurereturns (address adapter_,
bytes4 selector_,
bytesmemory integrationData_
)
{
returnabi.decode(_callArgs, (address, bytes4, bytes));
}
/// @dev Helper to emit the CallOnIntegrationExecuted event./// Avoids stack-too-deep error.function__emitCoIEvent(address _vaultProxy,
address _caller,
address _adapter,
bytes4 _selector,
bytesmemory _integrationData,
address[] memory _incomingAssets,
uint256[] memory _incomingAssetAmounts,
address[] memory _outgoingAssets,
uint256[] memory _outgoingAssetAmounts
) private{
emit CallOnIntegrationExecutedForFund(
msg.sender,
_vaultProxy,
_caller,
_adapter,
_selector,
_integrationData,
_incomingAssets,
_incomingAssetAmounts,
_outgoingAssets,
_outgoingAssetAmounts
);
}
/// @dev Helper to execute a call to an integration/// @dev Avoids stack-too-deep errorfunction__executeCoI(address _vaultProxy,
bytesmemory _callArgs,
bytesmemory _encodedAssetTransferArgs
) private{
(
address adapter,
bytes4 selector,
bytesmemory integrationData
) = __decodeCallOnIntegrationArgs(_callArgs);
(bool success, bytesmemory returnData) = adapter.call(
abi.encodeWithSelector(
selector,
_vaultProxy,
integrationData,
_encodedAssetTransferArgs
)
);
require(success, string(returnData));
}
/// @dev Helper to get the vault's balance of a particular assetfunction__getVaultAssetBalance(address _vaultProxy, address _asset)
privateviewreturns (uint256)
{
return ERC20(_asset).balanceOf(_vaultProxy);
}
/// @dev Helper to check if an asset is supportedfunction__isSupportedAsset(address _asset) privateviewreturns (bool isSupported_) {
return
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_asset) ||
IDerivativePriceFeed(DERIVATIVE_PRICE_FEED).isSupportedAsset(_asset);
}
/// @dev Helper for the actions to take on external contracts prior to executing CoIfunction__preCoIHook(address _adapter, bytes4 _selector) private{
IPolicyManager(POLICY_MANAGER).validatePolicies(
msg.sender,
IPolicyManager.PolicyHook.PreCallOnIntegration,
abi.encode(_adapter, _selector)
);
}
/// @dev Helper for the internal actions to take prior to executing CoIfunction__preProcessCoI(address _vaultProxy, bytesmemory _callArgs)
privatereturns (address[] memory expectedIncomingAssets_,
uint256[] memory preCallIncomingAssetBalances_,
uint256[] memory minIncomingAssetAmounts_,
SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory maxSpendAssetAmounts_,
uint256[] memory preCallSpendAssetBalances_
)
{
(
address adapter,
bytes4 selector,
bytesmemory integrationData
) = __decodeCallOnIntegrationArgs(_callArgs);
require(adapterIsRegistered(adapter), "callOnIntegration: Adapter is not registered");
// Note that expected incoming and spend assets are allowed to overlap// (e.g., a fee for the incomingAsset charged in a spend asset)
(
spendAssetsHandleType_,
spendAssets_,
maxSpendAssetAmounts_,
expectedIncomingAssets_,
minIncomingAssetAmounts_
) = IIntegrationAdapter(adapter).parseAssetsForMethod(selector, integrationData);
require(
spendAssets_.length== maxSpendAssetAmounts_.length,
"__preProcessCoI: Spend assets arrays unequal"
);
require(
expectedIncomingAssets_.length== minIncomingAssetAmounts_.length,
"__preProcessCoI: Incoming assets arrays unequal"
);
require(spendAssets_.isUniqueSet(), "__preProcessCoI: Duplicate spend asset");
require(
expectedIncomingAssets_.isUniqueSet(),
"__preProcessCoI: Duplicate incoming asset"
);
IVault vaultProxyContract = IVault(_vaultProxy);
preCallIncomingAssetBalances_ =newuint256[](expectedIncomingAssets_.length);
for (uint256 i =0; i < expectedIncomingAssets_.length; i++) {
require(
expectedIncomingAssets_[i] !=address(0),
"__preProcessCoI: Empty incoming asset address"
);
require(
minIncomingAssetAmounts_[i] >0,
"__preProcessCoI: minIncomingAssetAmount must be >0"
);
require(
__isSupportedAsset(expectedIncomingAssets_[i]),
"__preProcessCoI: Non-receivable incoming asset"
);
// Get pre-call balance of each incoming asset.// If the asset is not tracked by the fund, allow the balance to default to 0.if (vaultProxyContract.isTrackedAsset(expectedIncomingAssets_[i])) {
// We do not require incoming asset finality, but we attempt to finalize so that// the final incoming asset amount is more accurate. There is no need to finalize// post-tx.
preCallIncomingAssetBalances_[i] = __finalizeIfSynthAndGetAssetBalance(
_vaultProxy,
expectedIncomingAssets_[i],
false
);
}
}
// Get pre-call balances of spend assets and grant approvals to adapter
preCallSpendAssetBalances_ =newuint256[](spendAssets_.length);
for (uint256 i =0; i < spendAssets_.length; i++) {
require(spendAssets_[i] !=address(0), "__preProcessCoI: Empty spend asset");
require(maxSpendAssetAmounts_[i] >0, "__preProcessCoI: Empty max spend asset amount");
// A spend asset must either be a tracked asset of the fund or a supported asset,// in order to prevent seeding the fund with a malicious token and performing arbitrary// actions within an adapter.require(
vaultProxyContract.isTrackedAsset(spendAssets_[i]) ||
__isSupportedAsset(spendAssets_[i]),
"__preProcessCoI: Non-spendable spend asset"
);
// If spend asset is also an incoming asset, no need to record its balanceif (!expectedIncomingAssets_.contains(spendAssets_[i])) {
// By requiring spend asset finality before CoI, we will know whether or// not the asset balance was entirely spent during the call. There is no need// to finalize post-tx.
preCallSpendAssetBalances_[i] = __finalizeIfSynthAndGetAssetBalance(
_vaultProxy,
spendAssets_[i],
true
);
}
// Grant spend assets access to the adapter.// Note that spendAssets_ is already asserted to a unique set.if (spendAssetsHandleType_ == SpendAssetsHandleType.Approve) {
// Use exact approve amount rather than increasing allowances,// because all adapters finish their actions atomically.
__approveAssetSpender(
msg.sender,
spendAssets_[i],
adapter,
maxSpendAssetAmounts_[i]
);
} elseif (spendAssetsHandleType_ == SpendAssetsHandleType.Transfer) {
__withdrawAssetTo(msg.sender, spendAssets_[i], adapter, maxSpendAssetAmounts_[i]);
} elseif (spendAssetsHandleType_ == SpendAssetsHandleType.Remove) {
__removeTrackedAsset(msg.sender, spendAssets_[i]);
}
}
}
/// @dev Helper for the actions to take on external contracts after executing CoIfunction__postCoIHook(address _adapter,
bytes4 _selector,
address[] memory _incomingAssets,
uint256[] memory _incomingAssetAmounts,
address[] memory _outgoingAssets,
uint256[] memory _outgoingAssetAmounts
) private{
IPolicyManager(POLICY_MANAGER).validatePolicies(
msg.sender,
IPolicyManager.PolicyHook.PostCallOnIntegration,
abi.encode(
_adapter,
_selector,
_incomingAssets,
_incomingAssetAmounts,
_outgoingAssets,
_outgoingAssetAmounts
)
);
}
/// @dev Helper to reconcile and format incoming and outgoing assets after executing CoIfunction__postProcessCoI(address _vaultProxy,
address[] memory _expectedIncomingAssets,
uint256[] memory _preCallIncomingAssetBalances,
uint256[] memory _minIncomingAssetAmounts,
SpendAssetsHandleType _spendAssetsHandleType,
address[] memory _spendAssets,
uint256[] memory _maxSpendAssetAmounts,
uint256[] memory _preCallSpendAssetBalances
)
privatereturns (address[] memory incomingAssets_,
uint256[] memory incomingAssetAmounts_,
address[] memory outgoingAssets_,
uint256[] memory outgoingAssetAmounts_
)
{
address[] memory increasedSpendAssets;
uint256[] memory increasedSpendAssetAmounts;
(
outgoingAssets_,
outgoingAssetAmounts_,
increasedSpendAssets,
increasedSpendAssetAmounts
) = __reconcileCoISpendAssets(
_vaultProxy,
_spendAssetsHandleType,
_spendAssets,
_maxSpendAssetAmounts,
_preCallSpendAssetBalances
);
(incomingAssets_, incomingAssetAmounts_) = __reconcileCoIIncomingAssets(
_vaultProxy,
_expectedIncomingAssets,
_preCallIncomingAssetBalances,
_minIncomingAssetAmounts,
increasedSpendAssets,
increasedSpendAssetAmounts
);
return (incomingAssets_, incomingAssetAmounts_, outgoingAssets_, outgoingAssetAmounts_);
}
/// @dev Helper to process incoming asset balance changes./// See __reconcileCoISpendAssets() for explanation on "increasedSpendAssets".function__reconcileCoIIncomingAssets(address _vaultProxy,
address[] memory _expectedIncomingAssets,
uint256[] memory _preCallIncomingAssetBalances,
uint256[] memory _minIncomingAssetAmounts,
address[] memory _increasedSpendAssets,
uint256[] memory _increasedSpendAssetAmounts
) privatereturns (address[] memory incomingAssets_, uint256[] memory incomingAssetAmounts_) {
// Incoming assets = expected incoming assets + spend assets with increased balancesuint256 incomingAssetsCount = _expectedIncomingAssets.length.add(
_increasedSpendAssets.length
);
// Calculate and validate incoming asset amounts
incomingAssets_ =newaddress[](incomingAssetsCount);
incomingAssetAmounts_ =newuint256[](incomingAssetsCount);
for (uint256 i =0; i < _expectedIncomingAssets.length; i++) {
uint256 balanceDiff = __getVaultAssetBalance(_vaultProxy, _expectedIncomingAssets[i])
.sub(_preCallIncomingAssetBalances[i]);
require(
balanceDiff >= _minIncomingAssetAmounts[i],
"__reconcileCoIAssets: Received incoming asset less than expected"
);
// Even if the asset's previous balance was >0, it might not have been tracked
__addTrackedAsset(msg.sender, _expectedIncomingAssets[i]);
incomingAssets_[i] = _expectedIncomingAssets[i];
incomingAssetAmounts_[i] = balanceDiff;
}
// Append increaseSpendAssets to incomingAsset varsif (_increasedSpendAssets.length>0) {
uint256 incomingAssetIndex = _expectedIncomingAssets.length;
for (uint256 i =0; i < _increasedSpendAssets.length; i++) {
incomingAssets_[incomingAssetIndex] = _increasedSpendAssets[i];
incomingAssetAmounts_[incomingAssetIndex] = _increasedSpendAssetAmounts[i];
incomingAssetIndex++;
}
}
return (incomingAssets_, incomingAssetAmounts_);
}
/// @dev Helper to process spend asset balance changes./// "outgoingAssets" are the spend assets with a decrease in balance./// "increasedSpendAssets" are the spend assets with an unexpected increase in balance./// For example, "increasedSpendAssets" can occur if an adapter has a pre-balance of/// the spendAsset, which would be transferred to the fund at the end of the tx.function__reconcileCoISpendAssets(address _vaultProxy,
SpendAssetsHandleType _spendAssetsHandleType,
address[] memory _spendAssets,
uint256[] memory _maxSpendAssetAmounts,
uint256[] memory _preCallSpendAssetBalances
)
privatereturns (address[] memory outgoingAssets_,
uint256[] memory outgoingAssetAmounts_,
address[] memory increasedSpendAssets_,
uint256[] memory increasedSpendAssetAmounts_
)
{
// Determine spend asset balance changesuint256[] memory postCallSpendAssetBalances =newuint256[](_spendAssets.length);
uint256 outgoingAssetsCount;
uint256 increasedSpendAssetsCount;
for (uint256 i =0; i < _spendAssets.length; i++) {
// If spend asset's initial balance is 0, then it is an incoming assetif (_preCallSpendAssetBalances[i] ==0) {
continue;
}
// Handle SpendAssetsHandleType.Remove separatelyif (_spendAssetsHandleType == SpendAssetsHandleType.Remove) {
outgoingAssetsCount++;
continue;
}
// Determine if the asset is outgoing or incoming, and store the post-balance for later use
postCallSpendAssetBalances[i] = __getVaultAssetBalance(_vaultProxy, _spendAssets[i]);
// If the pre- and post- balances are equal, then the asset is neither incoming nor outgoingif (postCallSpendAssetBalances[i] < _preCallSpendAssetBalances[i]) {
outgoingAssetsCount++;
} elseif (postCallSpendAssetBalances[i] > _preCallSpendAssetBalances[i]) {
increasedSpendAssetsCount++;
}
}
// Format outgoingAssets and increasedSpendAssets (spend assets with unexpected increase in balance)
outgoingAssets_ =newaddress[](outgoingAssetsCount);
outgoingAssetAmounts_ =newuint256[](outgoingAssetsCount);
increasedSpendAssets_ =newaddress[](increasedSpendAssetsCount);
increasedSpendAssetAmounts_ =newuint256[](increasedSpendAssetsCount);
uint256 outgoingAssetsIndex;
uint256 increasedSpendAssetsIndex;
for (uint256 i =0; i < _spendAssets.length; i++) {
// If spend asset's initial balance is 0, then it is an incoming asset.if (_preCallSpendAssetBalances[i] ==0) {
continue;
}
// Handle SpendAssetsHandleType.Remove separately.// No need to validate the max spend asset amount.if (_spendAssetsHandleType == SpendAssetsHandleType.Remove) {
outgoingAssets_[outgoingAssetsIndex] = _spendAssets[i];
outgoingAssetAmounts_[outgoingAssetsIndex] = _preCallSpendAssetBalances[i];
outgoingAssetsIndex++;
continue;
}
// If the pre- and post- balances are equal, then the asset is neither incoming nor outgoingif (postCallSpendAssetBalances[i] < _preCallSpendAssetBalances[i]) {
if (postCallSpendAssetBalances[i] ==0) {
__removeTrackedAsset(msg.sender, _spendAssets[i]);
outgoingAssetAmounts_[outgoingAssetsIndex] = _preCallSpendAssetBalances[i];
} else {
outgoingAssetAmounts_[outgoingAssetsIndex] = _preCallSpendAssetBalances[i].sub(
postCallSpendAssetBalances[i]
);
}
require(
outgoingAssetAmounts_[outgoingAssetsIndex] <= _maxSpendAssetAmounts[i],
"__reconcileCoISpendAssets: Spent amount greater than expected"
);
outgoingAssets_[outgoingAssetsIndex] = _spendAssets[i];
outgoingAssetsIndex++;
} elseif (postCallSpendAssetBalances[i] > _preCallSpendAssetBalances[i]) {
increasedSpendAssetAmounts_[increasedSpendAssetsIndex] = postCallSpendAssetBalances[i]
.sub(_preCallSpendAssetBalances[i]);
increasedSpendAssets_[increasedSpendAssetsIndex] = _spendAssets[i];
increasedSpendAssetsIndex++;
}
}
return (
outgoingAssets_,
outgoingAssetAmounts_,
increasedSpendAssets_,
increasedSpendAssetAmounts_
);
}
///////////////////////////// INTEGRATIONS REGISTRY //////////////////////////////// @notice Remove integration adapters from the list of registered adapters/// @param _adapters Addresses of adapters to be deregisteredfunctionderegisterAdapters(address[] calldata _adapters) externalonlyFundDeployerOwner{
require(_adapters.length>0, "deregisterAdapters: _adapters cannot be empty");
for (uint256 i; i < _adapters.length; i++) {
require(
adapterIsRegistered(_adapters[i]),
"deregisterAdapters: Adapter is not registered"
);
registeredAdapters.remove(_adapters[i]);
emit AdapterDeregistered(_adapters[i], IIntegrationAdapter(_adapters[i]).identifier());
}
}
/// @notice Add integration adapters to the list of registered adapters/// @param _adapters Addresses of adapters to be registeredfunctionregisterAdapters(address[] calldata _adapters) externalonlyFundDeployerOwner{
require(_adapters.length>0, "registerAdapters: _adapters cannot be empty");
for (uint256 i; i < _adapters.length; i++) {
require(_adapters[i] !=address(0), "registerAdapters: Adapter cannot be empty");
require(
!adapterIsRegistered(_adapters[i]),
"registerAdapters: Adapter already registered"
);
registeredAdapters.add(_adapters[i]);
emit AdapterRegistered(_adapters[i], IIntegrationAdapter(_adapters[i]).identifier());
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Checks if an integration adapter is registered/// @param _adapter The adapter to check/// @return isRegistered_ True if the adapter is registeredfunctionadapterIsRegistered(address _adapter) publicviewreturns (bool isRegistered_) {
return registeredAdapters.contains(_adapter);
}
/// @notice Gets the `DERIVATIVE_PRICE_FEED` variable/// @return derivativePriceFeed_ The `DERIVATIVE_PRICE_FEED` variable valuefunctiongetDerivativePriceFeed() externalviewreturns (address derivativePriceFeed_) {
return DERIVATIVE_PRICE_FEED;
}
/// @notice Gets the `POLICY_MANAGER` variable/// @return policyManager_ The `POLICY_MANAGER` variable valuefunctiongetPolicyManager() externalviewreturns (address policyManager_) {
return POLICY_MANAGER;
}
/// @notice Gets the `PRIMITIVE_PRICE_FEED` variable/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable valuefunctiongetPrimitivePriceFeed() externalviewreturns (address primitivePriceFeed_) {
return PRIMITIVE_PRICE_FEED;
}
/// @notice Gets all registered integration adapters/// @return registeredAdaptersArray_ A list of all registered integration adaptersfunctiongetRegisteredAdapters()
externalviewreturns (address[] memory registeredAdaptersArray_)
{
registeredAdaptersArray_ =newaddress[](registeredAdapters.length());
for (uint256 i =0; i < registeredAdaptersArray_.length; i++) {
registeredAdaptersArray_[i] = registeredAdapters.at(i);
}
return registeredAdaptersArray_;
}
}
Contract Source Code
File 79 of 138: IntegrationSelectors.sol
// 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.
*/pragmasolidity 0.6.12;/// @title IntegrationSelectors Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Selectors for integration actions/// @dev Selectors are created from their signatures rather than hardcoded for easy verificationabstractcontractIntegrationSelectors{
bytes4publicconstant ADD_TRACKED_ASSETS_SELECTOR =bytes4(
keccak256("addTrackedAssets(address,bytes,bytes)")
);
// Tradingbytes4publicconstant TAKE_ORDER_SELECTOR =bytes4(
keccak256("takeOrder(address,bytes,bytes)")
);
// Lendingbytes4publicconstant LEND_SELECTOR =bytes4(keccak256("lend(address,bytes,bytes)"));
bytes4publicconstant REDEEM_SELECTOR =bytes4(keccak256("redeem(address,bytes,bytes)"));
}
Contract Source Code
File 80 of 138: InvestorWhitelist.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/AddressListPolicyMixin.sol";
import"./utils/PreBuySharesValidatePolicyBase.sol";
/// @title InvestorWhitelist Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that only allows a configurable whitelist of investors to buy shares in a fundcontractInvestorWhitelistisPreBuySharesValidatePolicyBase, AddressListPolicyMixin{
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Adds the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
__updateList(_comptrollerProxy, _encodedSettings);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"INVESTOR_WHITELIST";
}
/// @notice Updates the policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionupdateFundSettings(address _comptrollerProxy,
address,
bytescalldata _encodedSettings
) externaloverrideonlyPolicyManager{
__updateList(_comptrollerProxy, _encodedSettings);
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _investor The investor for which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, address _investor)
publicviewreturns (bool isValid_)
{
return isInList(_comptrollerProxy, _investor);
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(address buyer, , , ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, buyer);
}
/// @dev Helper to update the investor whitelist by adding and/or removing addressesfunction__updateList(address _comptrollerProxy, bytesmemory _settingsData) private{
(address[] memory itemsToAdd, address[] memory itemsToRemove) =abi.decode(
_settingsData,
(address[], address[])
);
// If an address is in both add and remove arrays, they will not be in the final list.// We do not check for uniqueness between the two arrays for efficiency.if (itemsToAdd.length>0) {
__addToList(_comptrollerProxy, itemsToAdd);
}
if (itemsToRemove.length>0) {
__removeFromList(_comptrollerProxy, itemsToRemove);
}
}
}
Contract Source Code
File 81 of 138: KyberAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../../../interfaces/IKyberNetworkProxy.sol";
import"../../../../interfaces/IWETH.sol";
import"../../../../utils/MathHelpers.sol";
import"../utils/AdapterBase.sol";
/// @title KyberAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for interacting with Kyber NetworkcontractKyberAdapterisAdapterBase, MathHelpers{
addressprivateimmutable EXCHANGE;
addressprivateimmutable WETH_TOKEN;
constructor(address _integrationManager,
address _exchange,
address _wethToken
) publicAdapterBase(_integrationManager) {
EXCHANGE = _exchange;
WETH_TOKEN = _wethToken;
}
/// @dev Needed to receive ETH from swapreceive() externalpayable{}
// EXTERNAL FUNCTIONS/// @notice Provides a constant string identifier for an adapter/// @return identifier_ An identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"KYBER_NETWORK";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
require(_selector == TAKE_ORDER_SELECTOR, "parseAssetsForMethod: _selector invalid");
(
address incomingAsset,
uint256 minIncomingAssetAmount,
address outgoingAsset,
uint256 outgoingAssetAmount
) = __decodeCallArgs(_encodedCallArgs);
require(
incomingAsset != outgoingAsset,
"parseAssetsForMethod: incomingAsset and outgoingAsset asset cannot be the same"
);
require(outgoingAssetAmount >0, "parseAssetsForMethod: outgoingAssetAmount must be >0");
spendAssets_ =newaddress[](1);
spendAssets_[0] = outgoingAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = incomingAsset;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minIncomingAssetAmount;
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Trades assets on Kyber/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctiontakeOrder(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(
address incomingAsset,
uint256 minIncomingAssetAmount,
address outgoingAsset,
uint256 outgoingAssetAmount
) = __decodeCallArgs(_encodedCallArgs);
uint256 minExpectedRate = __calcNormalizedRate(
ERC20(outgoingAsset).decimals(),
outgoingAssetAmount,
ERC20(incomingAsset).decimals(),
minIncomingAssetAmount
);
if (outgoingAsset == WETH_TOKEN) {
__swapNativeAssetToToken(incomingAsset, outgoingAssetAmount, minExpectedRate);
} elseif (incomingAsset == WETH_TOKEN) {
__swapTokenToNativeAsset(outgoingAsset, outgoingAssetAmount, minExpectedRate);
} else {
__swapTokenToToken(incomingAsset, outgoingAsset, outgoingAssetAmount, minExpectedRate);
}
}
// PRIVATE FUNCTIONS/// @dev Helper to decode the encoded call argumentsfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address incomingAsset_,
uint256 minIncomingAssetAmount_,
address outgoingAsset_,
uint256 outgoingAssetAmount_
)
{
returnabi.decode(_encodedCallArgs, (address, uint256, address, uint256));
}
/// @dev Executes a swap of ETH to ERC20function__swapNativeAssetToToken(address _incomingAsset,
uint256 _outgoingAssetAmount,
uint256 _minExpectedRate
) private{
IWETH(payable(WETH_TOKEN)).withdraw(_outgoingAssetAmount);
IKyberNetworkProxy(EXCHANGE).swapEtherToToken{value: _outgoingAssetAmount}(
_incomingAsset,
_minExpectedRate
);
}
/// @dev Executes a swap of ERC20 to ETHfunction__swapTokenToNativeAsset(address _outgoingAsset,
uint256 _outgoingAssetAmount,
uint256 _minExpectedRate
) private{
__approveMaxAsNeeded(_outgoingAsset, EXCHANGE, _outgoingAssetAmount);
IKyberNetworkProxy(EXCHANGE).swapTokenToEther(
_outgoingAsset,
_outgoingAssetAmount,
_minExpectedRate
);
IWETH(payable(WETH_TOKEN)).deposit{value: payable(address(this)).balance}();
}
/// @dev Executes a swap of ERC20 to ERC20function__swapTokenToToken(address _incomingAsset,
address _outgoingAsset,
uint256 _outgoingAssetAmount,
uint256 _minExpectedRate
) private{
__approveMaxAsNeeded(_outgoingAsset, EXCHANGE, _outgoingAssetAmount);
IKyberNetworkProxy(EXCHANGE).swapTokenToToken(
_outgoingAsset,
_outgoingAssetAmount,
_incomingAsset,
_minExpectedRate
);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `EXCHANGE` variable/// @return exchange_ The `EXCHANGE` variable valuefunctiongetExchange() externalviewreturns (address exchange_) {
return EXCHANGE;
}
/// @notice Gets the `WETH_TOKEN` variable/// @return wethToken_ The `WETH_TOKEN` variable valuefunctiongetWethToken() externalviewreturns (address wethToken_) {
return WETH_TOKEN;
}
}
Contract Source Code
File 82 of 138: MakerDaoMath.sol
// SPDX-License-Identifier: AGPL-3.0-or-later// Copyright (C) 2018 Rain <rainbreak@riseup.net>//// This program is free software: you can redistribute it and/or modify// it under the terms of the GNU Affero General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Affero General Public License for more details.//// You should have received a copy of the GNU Affero General Public License// along with this program. If not, see <https://www.gnu.org/licenses/>.pragmasolidity 0.6.12;/// @title MakerDaoMath Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Helper functions for math operations adapted from MakerDao contractsabstractcontractMakerDaoMath{
/// @dev Performs scaled, fixed-point exponentiation./// Verbatim code, adapted to our style guide for variable naming only, see:/// https://github.com/makerdao/dss/blob/master/src/pot.sol#L83-L105// prettier-ignorefunction__rpow(uint256 _x, uint256 _n, uint256 _base) internalpurereturns (uint256 z_) {
assembly {
switch _x case0 {switch _n case0 {z_ := _base} default {z_ :=0}}
default {
switchmod(_n, 2) case0 { z_ := _base } default { z_ := _x }
let half :=div(_base, 2)
for { _n :=div(_n, 2) } _n { _n :=div(_n,2) } {
let xx :=mul(_x, _x)
ifiszero(eq(div(xx, _x), _x)) { revert(0,0) }
let xxRound :=add(xx, half)
iflt(xxRound, xx) { revert(0,0) }
_x :=div(xxRound, _base)
ifmod(_n,2) {
let zx :=mul(z_, _x)
ifand(iszero(iszero(_x)), iszero(eq(div(zx, _x), z_))) { revert(0,0) }
let zxRound :=add(zx, half)
iflt(zxRound, zx) { revert(0,0) }
z_ :=div(zxRound, _base)
}
}
}
}
return z_;
}
}
Contract Source Code
File 83 of 138: ManagementFee.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../core/fund/vault/VaultLib.sol";
import"../../../utils/MakerDaoMath.sol";
import"./utils/FeeBase.sol";
/// @title ManagementFee Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A management fee with a configurable annual ratecontractManagementFeeisFeeBase, MakerDaoMath{
usingSafeMathforuint256;
eventFundSettingsAdded(addressindexed comptrollerProxy, uint256 scaledPerSecondRate);
eventSettled(addressindexed comptrollerProxy,
uint256 sharesQuantity,
uint256 secondsSinceSettlement
);
structFeeInfo {
uint256 scaledPerSecondRate;
uint256 lastSettled;
}
uint256privateconstant RATE_SCALE_BASE =10**27;
mapping(address=> FeeInfo) private comptrollerProxyToFeeInfo;
constructor(address _feeManager) publicFeeBase(_feeManager) {}
// EXTERNAL FUNCTIONS/// @notice Activates the fee for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _vaultProxy The VaultProxy of the fundfunctionactivateForFund(address _comptrollerProxy, address _vaultProxy)
externaloverrideonlyFeeManager{
// It is only necessary to set `lastSettled` for a migrated fundif (VaultLib(_vaultProxy).totalSupply() >0) {
comptrollerProxyToFeeInfo[_comptrollerProxy].lastSettled =block.timestamp;
}
}
/// @notice Add the initial fee settings for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _settingsData Encoded settings to apply to the fee for a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _settingsData)
externaloverrideonlyFeeManager{
uint256 scaledPerSecondRate =abi.decode(_settingsData, (uint256));
require(
scaledPerSecondRate >0,
"addFundSettings: scaledPerSecondRate must be greater than 0"
);
comptrollerProxyToFeeInfo[_comptrollerProxy] = FeeInfo({
scaledPerSecondRate: scaledPerSecondRate,
lastSettled: 0
});
emit FundSettingsAdded(_comptrollerProxy, scaledPerSecondRate);
}
/// @notice Provides a constant string identifier for a fee/// @return identifier_ The identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"MANAGEMENT";
}
/// @notice Gets the hooks that are implemented by the fee/// @return implementedHooksForSettle_ The hooks during which settle() is implemented/// @return implementedHooksForUpdate_ The hooks during which update() is implemented/// @return usesGavOnSettle_ True if GAV is used during the settle() implementation/// @return usesGavOnUpdate_ True if GAV is used during the update() implementation/// @dev Used only during fee registrationfunctionimplementedHooks()
externalviewoverridereturns (
IFeeManager.FeeHook[] memory implementedHooksForSettle_,
IFeeManager.FeeHook[] memory implementedHooksForUpdate_,
bool usesGavOnSettle_,
bool usesGavOnUpdate_
)
{
implementedHooksForSettle_ =new IFeeManager.FeeHook[](3);
implementedHooksForSettle_[0] = IFeeManager.FeeHook.Continuous;
implementedHooksForSettle_[1] = IFeeManager.FeeHook.BuySharesSetup;
implementedHooksForSettle_[2] = IFeeManager.FeeHook.PreRedeemShares;
return (implementedHooksForSettle_, new IFeeManager.FeeHook[](0), false, false);
}
/// @notice Settle the fee and calculate shares due/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _vaultProxy The VaultProxy of the fund/// @return settlementType_ The type of settlement/// @return (unused) The payer of shares due/// @return sharesDue_ The amount of shares duefunctionsettle(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook,
bytescalldata,
uint256)
externaloverrideonlyFeeManagerreturns (
IFeeManager.SettlementType settlementType_,
address,
uint256 sharesDue_
)
{
FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy];
// If this fee was settled in the current block, we can return earlyuint256 secondsSinceSettlement =block.timestamp.sub(feeInfo.lastSettled);
if (secondsSinceSettlement ==0) {
return (IFeeManager.SettlementType.None, address(0), 0);
}
// If there are shares issued for the fund, calculate the shares due
VaultLib vaultProxyContract = VaultLib(_vaultProxy);
uint256 sharesSupply = vaultProxyContract.totalSupply();
if (sharesSupply >0) {
// This assumes that all shares in the VaultProxy are shares outstanding,// which is fine for this release. Even if they are not, they are still shares that// are only claimable by the fund owner.uint256 netSharesSupply = sharesSupply.sub(vaultProxyContract.balanceOf(_vaultProxy));
if (netSharesSupply >0) {
sharesDue_ = netSharesSupply
.mul(
__rpow(feeInfo.scaledPerSecondRate, secondsSinceSettlement, RATE_SCALE_BASE)
.sub(RATE_SCALE_BASE)
)
.div(RATE_SCALE_BASE);
}
}
// Must settle even when no shares are due, for the case that settlement is being// done when there are no shares in the fund (i.e. at the first investment, or at the// first investment after all shares have been redeemed)
comptrollerProxyToFeeInfo[_comptrollerProxy].lastSettled =block.timestamp;
emit Settled(_comptrollerProxy, sharesDue_, secondsSinceSettlement);
if (sharesDue_ ==0) {
return (IFeeManager.SettlementType.None, address(0), 0);
}
return (IFeeManager.SettlementType.Mint, address(0), sharesDue_);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the feeInfo for a given fund/// @param _comptrollerProxy The ComptrollerProxy contract of the fund/// @return feeInfo_ The feeInfofunctiongetFeeInfoForFund(address _comptrollerProxy)
externalviewreturns (FeeInfo memory feeInfo_)
{
return comptrollerProxyToFeeInfo[_comptrollerProxy];
}
}
Contract Source Code
File 84 of 138: MathHelpers.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
/// @title MathHelpers Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Helper functions for common math operationsabstractcontractMathHelpers{
usingSafeMathforuint256;
/// @dev Calculates a proportional value relative to a known ratiofunction__calcRelativeQuantity(uint256 _quantity1,
uint256 _quantity2,
uint256 _relativeQuantity1
) internalpurereturns (uint256 relativeQuantity2_) {
return _relativeQuantity1.mul(_quantity2).div(_quantity1);
}
/// @dev Calculates a rate normalized to 10^18 precision,/// for given base and quote asset decimals and amountsfunction__calcNormalizedRate(uint256 _baseAssetDecimals,
uint256 _baseAssetAmount,
uint256 _quoteAssetDecimals,
uint256 _quoteAssetAmount
) internalpurereturns (uint256 normalizedRate_) {
return
_quoteAssetAmount.mul(10**_baseAssetDecimals.add(18)).div(
_baseAssetAmount.mul(10**_quoteAssetDecimals)
);
}
}
Contract Source Code
File 85 of 138: MaxConcentration.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../../../core/fund/comptroller/ComptrollerLib.sol";
import"../../../../core/fund/vault/VaultLib.sol";
import"../../../../infrastructure/value-interpreter/ValueInterpreter.sol";
import"./utils/PostCallOnIntegrationValidatePolicyBase.sol";
/// @title MaxConcentration Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that defines a configurable threshold for the concentration of any one asset/// in a fund's holdingscontractMaxConcentrationisPostCallOnIntegrationValidatePolicyBase{
usingSafeMathforuint256;
eventMaxConcentrationSet(addressindexed comptrollerProxy, uint256 value);
uint256privateconstant ONE_HUNDRED_PERCENT =10**18; // 100%addressprivateimmutable VALUE_INTERPRETER;
mapping(address=>uint256) private comptrollerProxyToMaxConcentration;
constructor(address _policyManager, address _valueInterpreter)
publicPolicyBase(_policyManager)
{
VALUE_INTERPRETER = _valueInterpreter;
}
/// @notice Validates and initializes a policy as necessary prior to fund activation/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _vaultProxy The fund's VaultProxy address/// @dev No need to authenticate access, as there are no state transitionsfunctionactivateForFund(address _comptrollerProxy, address _vaultProxy)
externaloverrideonlyPolicyManager{
require(
passesRule(_comptrollerProxy, _vaultProxy, VaultLib(_vaultProxy).getTrackedAssets()),
"activateForFund: Max concentration exceeded"
);
}
/// @notice Add the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
uint256 maxConcentration =abi.decode(_encodedSettings, (uint256));
require(maxConcentration >0, "addFundSettings: maxConcentration must be greater than 0");
require(
maxConcentration <= ONE_HUNDRED_PERCENT,
"addFundSettings: maxConcentration cannot exceed 100%"
);
comptrollerProxyToMaxConcentration[_comptrollerProxy] = maxConcentration;
emit MaxConcentrationSet(_comptrollerProxy, maxConcentration);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"MAX_CONCENTRATION";
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _vaultProxy The fund's VaultProxy address/// @param _assets The assets with which to check the rule/// @return isValid_ True if the rule passes/// @dev The fund's denomination asset is exempt from the policy limit.functionpassesRule(address _comptrollerProxy,
address _vaultProxy,
address[] memory _assets
) publicreturns (bool isValid_) {
uint256 maxConcentration = comptrollerProxyToMaxConcentration[_comptrollerProxy];
ComptrollerLib comptrollerProxyContract = ComptrollerLib(_comptrollerProxy);
address denominationAsset = comptrollerProxyContract.getDenominationAsset();
// Does not require asset finality, otherwise will fail when incoming asset is a Synth
(uint256 totalGav, bool gavIsValid) = comptrollerProxyContract.calcGav(false);
if (!gavIsValid) {
returnfalse;
}
for (uint256 i =0; i < _assets.length; i++) {
address asset = _assets[i];
if (
!__rulePassesForAsset(
_vaultProxy,
denominationAsset,
maxConcentration,
totalGav,
asset
)
) {
returnfalse;
}
}
returntrue;
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _vaultProxy The fund's VaultProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address _vaultProxy,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(, , address[] memory incomingAssets, , , ) = __decodeRuleArgs(_encodedArgs);
if (incomingAssets.length==0) {
returntrue;
}
return passesRule(_comptrollerProxy, _vaultProxy, incomingAssets);
}
/// @dev Helper to check if the rule holds for a particular asset./// Avoids the stack-too-deep error.function__rulePassesForAsset(address _vaultProxy,
address _denominationAsset,
uint256 _maxConcentration,
uint256 _totalGav,
address _incomingAsset
) privatereturns (bool isValid_) {
if (_incomingAsset == _denominationAsset) returntrue;
uint256 assetBalance = ERC20(_incomingAsset).balanceOf(_vaultProxy);
(uint256 assetGav, bool assetGavIsValid) = ValueInterpreter(VALUE_INTERPRETER)
.calcLiveAssetValue(_incomingAsset, assetBalance, _denominationAsset);
if (
!assetGavIsValid ||
assetGav.mul(ONE_HUNDRED_PERCENT).div(_totalGav) > _maxConcentration
) {
returnfalse;
}
returntrue;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the maxConcentration for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return maxConcentration_ The maxConcentrationfunctiongetMaxConcentrationForFund(address _comptrollerProxy)
externalviewreturns (uint256 maxConcentration_)
{
return comptrollerProxyToMaxConcentration[_comptrollerProxy];
}
/// @notice Gets the `VALUE_INTERPRETER` variable/// @return valueInterpreter_ The `VALUE_INTERPRETER` variable valuefunctiongetValueInterpreter() externalviewreturns (address valueInterpreter_) {
return VALUE_INTERPRETER;
}
}
Contract Source Code
File 86 of 138: MinMaxInvestment.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"./utils/PreBuySharesValidatePolicyBase.sol";
/// @title MinMaxInvestment Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A policy that restricts the amount of the fund's denomination asset that a user can/// send in a single call to buy shares in a fundcontractMinMaxInvestmentisPreBuySharesValidatePolicyBase{
eventFundSettingsSet(addressindexed comptrollerProxy,
uint256 minInvestmentAmount,
uint256 maxInvestmentAmount
);
structFundSettings {
uint256 minInvestmentAmount;
uint256 maxInvestmentAmount;
}
mapping(address=> FundSettings) private comptrollerProxyToFundSettings;
constructor(address _policyManager) publicPolicyBase(_policyManager) {}
/// @notice Adds the initial policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _encodedSettings)
externaloverrideonlyPolicyManager{
__setFundSettings(_comptrollerProxy, _encodedSettings);
}
/// @notice Provides a constant string identifier for a policy/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"MIN_MAX_INVESTMENT";
}
/// @notice Updates the policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunctionupdateFundSettings(address _comptrollerProxy,
address,
bytescalldata _encodedSettings
) externaloverrideonlyPolicyManager{
__setFundSettings(_comptrollerProxy, _encodedSettings);
}
/// @notice Checks whether a particular condition passes the rule for a particular fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _investmentAmount The investment amount for which to check the rule/// @return isValid_ True if the rule passesfunctionpassesRule(address _comptrollerProxy, uint256 _investmentAmount)
publicviewreturns (bool isValid_)
{
uint256 minInvestmentAmount = comptrollerProxyToFundSettings[_comptrollerProxy]
.minInvestmentAmount;
uint256 maxInvestmentAmount = comptrollerProxyToFundSettings[_comptrollerProxy]
.maxInvestmentAmount;
// Both minInvestmentAmount and maxInvestmentAmount can be 0 in order to close the fund// temporarilyif (minInvestmentAmount ==0) {
return _investmentAmount <= maxInvestmentAmount;
} elseif (maxInvestmentAmount ==0) {
return _investmentAmount >= minInvestmentAmount;
}
return
_investmentAmount >= minInvestmentAmount && _investmentAmount <= maxInvestmentAmount;
}
/// @notice Apply the rule with the specified parameters of a PolicyHook/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedArgs Encoded args with which to validate the rule/// @return isValid_ True if the rule passesfunctionvalidateRule(address _comptrollerProxy,
address,
IPolicyManager.PolicyHook,
bytescalldata _encodedArgs
) externaloverridereturns (bool isValid_) {
(, uint256 investmentAmount, , ) = __decodeRuleArgs(_encodedArgs);
return passesRule(_comptrollerProxy, investmentAmount);
}
/// @dev Helper to set the policy settings for a fund/// @param _comptrollerProxy The fund's ComptrollerProxy address/// @param _encodedSettings Encoded settings to apply to a fundfunction__setFundSettings(address _comptrollerProxy, bytesmemory _encodedSettings) private{
(uint256 minInvestmentAmount, uint256 maxInvestmentAmount) =abi.decode(
_encodedSettings,
(uint256, uint256)
);
require(
maxInvestmentAmount ==0|| minInvestmentAmount < maxInvestmentAmount,
"__setFundSettings: minInvestmentAmount must be less than maxInvestmentAmount"
);
comptrollerProxyToFundSettings[_comptrollerProxy]
.minInvestmentAmount = minInvestmentAmount;
comptrollerProxyToFundSettings[_comptrollerProxy]
.maxInvestmentAmount = maxInvestmentAmount;
emit FundSettingsSet(_comptrollerProxy, minInvestmentAmount, maxInvestmentAmount);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the min and max investment amount for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return fundSettings_ The fund settingsfunctiongetFundSettings(address _comptrollerProxy)
externalviewreturns (FundSettings memory fundSettings_)
{
return comptrollerProxyToFundSettings[_comptrollerProxy];
}
}
Contract Source Code
File 87 of 138: MockCEtherIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;import"./MockCTokenBase.sol";
contractMockCEtherIntegrateeisMockCTokenBase{
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
address _weth,
address _centralizedRateProvider,
uint256 _initialRate
)
publicMockCTokenBase(_name, _symbol, _decimals, _weth, _centralizedRateProvider, _initialRate)
{}
functionmint() externalpayable{
uint256 amount =msg.value;
uint256 destAmount = __calcCTokenAmount(amount);
__swapAssets(msg.sender, ETH_ADDRESS, amount, address(this), destAmount);
}
functionredeem(uint256 _amount) externalreturns (uint256) {
uint256 destAmount = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER).calcLiveAssetValue(
address(this),
_amount,
TOKEN
);
__swapAssets(msg.sender, address(this), _amount, ETH_ADDRESS, destAmount);
return _amount;
}
}
Contract Source Code
File 88 of 138: MockCTokenBase.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import"../../prices/CentralizedRateProvider.sol";
import"../../utils/SwapperBase.sol";
contractMockCTokenBaseisERC20, SwapperBase, Ownable{
addressinternalimmutable TOKEN;
addressinternalimmutable CENTRALIZED_RATE_PROVIDER;
uint256internal rate;
mapping(address=>mapping(address=>uint256)) internal _allowances;
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
address _token,
address _centralizedRateProvider,
uint256 _initialRate
) publicERC20(_name, _symbol) {
_setupDecimals(_decimals);
TOKEN = _token;
CENTRALIZED_RATE_PROVIDER = _centralizedRateProvider;
rate = _initialRate;
}
functionapprove(address _spender, uint256 _amount) publicvirtualoverridereturns (bool) {
_allowances[msg.sender][_spender] = _amount;
returntrue;
}
/// @dev Overriden `allowance` function, give the integratee infinite approval by defaultfunctionallowance(address _owner, address _spender) publicviewoverridereturns (uint256) {
if (_spender ==address(this) || _owner == _spender) {
return2**256-1;
} else {
return _allowances[_owner][_spender];
}
}
/// @dev Necessary as this contract doesn't directly inherit from MockTokenfunctionmintFor(address _who, uint256 _amount) externalonlyOwner{
_mint(_who, _amount);
}
/// @dev Necessary to allow updates on persistent deployments (e.g Kovan)functionsetRate(uint256 _rate) publiconlyOwner{
rate = _rate;
}
functiontransferFrom(address _sender,
address _recipient,
uint256 _amount
) publicvirtualoverridereturns (bool) {
_transfer(_sender, _recipient, _amount);
returntrue;
}
// INTERNAL FUNCTIONS/// @dev Calculates the cTokenAmount given a tokenAmount/// Makes use of a inverse rate with the CentralizedRateProvider as a derivative can't be used as quoteAssetfunction__calcCTokenAmount(uint256 _tokenAmount) internalreturns (uint256 cTokenAmount_) {
uint256 tokenDecimals = ERC20(TOKEN).decimals();
uint256 cTokenDecimals = decimals();
// Result in Token Decimalsuint256 tokenPerCTokenUnit = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(address(this), 10**uint256(cTokenDecimals), TOKEN);
// Result in cToken decimalsuint256 inverseRate =uint256(10**tokenDecimals).mul(10**uint256(cTokenDecimals)).div(
tokenPerCTokenUnit
);
// Amount in token decimals, result in cToken decimals
cTokenAmount_ = _tokenAmount.mul(inverseRate).div(10**tokenDecimals);
}
///////////////////// STATE GETTERS //////////////////////// @dev Part of ICERC20 token interfacefunctionunderlying() publicviewreturns (address) {
return TOKEN;
}
/// @dev Part of ICERC20 token interface./// Called from CompoundPriceFeed, returns the actual Rate cToken/TokenfunctionexchangeRateStored() publicviewreturns (uint256) {
return rate;
}
functiongetCentralizedRateProvider() publicviewreturns (address) {
return CENTRALIZED_RATE_PROVIDER;
}
}
Contract Source Code
File 89 of 138: MockCTokenIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;import"./MockCTokenBase.sol";
contractMockCTokenIntegrateeisMockCTokenBase{
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
address _token,
address _centralizedRateProvider,
uint256 _initialRate
)
publicMockCTokenBase(_name, _symbol, _decimals, _token, _centralizedRateProvider, _initialRate)
{}
functionmint(uint256 _amount) externalreturns (uint256) {
uint256 destAmount = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER).calcLiveAssetValue(
TOKEN,
_amount,
address(this)
);
__swapAssets(msg.sender, TOKEN, _amount, address(this), destAmount);
return _amount;
}
functionredeem(uint256 _amount) externalreturns (uint256) {
uint256 destAmount = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER).calcLiveAssetValue(
address(this),
_amount,
TOKEN
);
__swapAssets(msg.sender, address(this), _amount, TOKEN, destAmount);
return _amount;
}
}
Contract Source Code
File 90 of 138: MockChaiIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;import"../prices/CentralizedRateProvider.sol";
import"../tokens/MockToken.sol";
import"../utils/SwapperBase.sol";
contractMockChaiIntegrateeisMockToken, SwapperBase{
addressprivateimmutable CENTRALIZED_RATE_PROVIDER;
addresspublicimmutable DAI;
constructor(address _dai,
address _centralizedRateProvider,
uint8 _decimals
) publicMockToken("Chai", "CHAI", _decimals) {
_setupDecimals(_decimals);
CENTRALIZED_RATE_PROVIDER = _centralizedRateProvider;
DAI = _dai;
}
functionjoin(address, uint256 _daiAmount) external{
uint256 tokenDecimals = ERC20(DAI).decimals();
uint256 chaiDecimals = decimals();
// Calculate the amount of tokens per one unit of DAIuint256 daiPerChaiUnit = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(address(this), 10**uint256(chaiDecimals), DAI);
// Calculate the inverse rate to know the amount of CHAI to return from a unit of DAIuint256 inverseRate =uint256(10**tokenDecimals).mul(10**uint256(chaiDecimals)).div(
daiPerChaiUnit
);
// Mint and send those CHAI to senderuint256 destAmount = _daiAmount.mul(inverseRate).div(10**tokenDecimals);
_mint(address(this), destAmount);
__swapAssets(msg.sender, DAI, _daiAmount, address(this), destAmount);
}
functionexit(addresspayable _trader, uint256 _chaiAmount) external{
uint256 destAmount = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER).calcLiveAssetValue(
address(this),
_chaiAmount,
DAI
);
// Burn CHAI of the trader.
_burn(_trader, _chaiAmount);
// Release DAI to the trader.
ERC20(DAI).transfer(msg.sender, destAmount);
}
}
Contract Source Code
File 91 of 138: MockChaiPriceSource.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
contractMockChaiPriceSource{
usingSafeMathforuint256;
uint256private chiStored =10**27;
uint256private rhoStored =now;
functiondrip() externalreturns (uint256) {
require(now>= rhoStored, "drip: invalid now");
rhoStored =now;
chiStored = chiStored.mul(99).div(100);
return chi();
}
////////////////////// STATE GETTERS /////////////////////functionchi() publicviewreturns (uint256) {
return chiStored;
}
functionrho() publicviewreturns (uint256) {
return rhoStored;
}
}
Contract Source Code
File 92 of 138: MockChainlinkPriceSource.sol
// 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.
*/pragmasolidity 0.6.12;contractMockChainlinkPriceSource{
eventAnswerUpdated(int256indexed current, uint256indexed roundId, uint256 timestamp);
uint256public DECIMALS;
int256public latestAnswer;
uint256public latestTimestamp;
uint256public roundId;
addresspublic aggregator;
constructor(uint256 _decimals) public{
DECIMALS = _decimals;
latestAnswer =int256(10**_decimals);
latestTimestamp =now;
roundId =1;
aggregator =address(this);
}
functionsetLatestAnswer(int256 _nextAnswer, uint256 _nextTimestamp) external{
latestAnswer = _nextAnswer;
latestTimestamp = _nextTimestamp;
roundId = roundId +1;
emit AnswerUpdated(latestAnswer, roundId, latestTimestamp);
}
functionsetAggregator(address _nextAggregator) external{
aggregator = _nextAggregator;
}
functiondecimals() publicviewreturns (uint256) {
return DECIMALS;
}
}
Contract Source Code
File 93 of 138: MockGenericAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../release/extensions/integration-manager/integrations/utils/AdapterBase.sol";
/// @title IMockGenericIntegratee Interface/// @author Enzyme Council <security@enzyme.finance>interfaceIMockGenericIntegratee{
functionswap(address[] calldata,
uint256[] calldata,
address[] calldata,
uint256[] calldata) externalpayable;
functionswapOnBehalf(addresspayable,
address[] calldata,
uint256[] calldata,
address[] calldata,
uint256[] calldata) externalpayable;
}
/// @title MockGenericAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Provides a generic adapter that:/// 1. Provides swapping functions that use various `SpendAssetsTransferType` values/// 2. Directly parses the _actual_ values to swap from provided call data (e.g., `actualIncomingAssetAmounts`)/// 3. Directly parses values needed by the IntegrationManager from provided call data (e.g., `minIncomingAssetAmounts`)contractMockGenericAdapterisAdapterBase{
addresspublicimmutable INTEGRATEE;
// No need to specify the IntegrationManagerconstructor(address _integratee) publicAdapterBase(address(0)) {
INTEGRATEE = _integratee;
}
functionidentifier() externalpureoverridereturns (stringmemory) {
return"MOCK_GENERIC";
}
functionparseAssetsForMethod(bytes4 _selector, bytescalldata _callArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory maxSpendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
(
spendAssets_,
maxSpendAssetAmounts_,
,
incomingAssets_,
minIncomingAssetAmounts_,
) = __decodeCallArgs(_callArgs);
return (
__getSpendAssetsHandleTypeForSelector(_selector),
spendAssets_,
maxSpendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @dev Assumes SpendAssetsHandleType.Transfer unless otherwise specifiedfunction__getSpendAssetsHandleTypeForSelector(bytes4 _selector)
privatepurereturns (IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_)
{
if (_selector ==bytes4(keccak256("removeOnly(address,bytes,bytes)"))) {
return IIntegrationManager.SpendAssetsHandleType.Remove;
}
if (_selector ==bytes4(keccak256("swapDirectFromVault(address,bytes,bytes)"))) {
return IIntegrationManager.SpendAssetsHandleType.None;
}
if (_selector ==bytes4(keccak256("swapViaApproval(address,bytes,bytes)"))) {
return IIntegrationManager.SpendAssetsHandleType.Approve;
}
return IIntegrationManager.SpendAssetsHandleType.Transfer;
}
functionremoveOnly(address,
bytescalldata,
bytescalldata) external{}
functionswapA(address _vaultProxy,
bytescalldata _callArgs,
bytescalldata _assetTransferArgs
) externalfundAssetsTransferHandler(_vaultProxy, _assetTransferArgs) {
__decodeCallArgsAndSwap(_callArgs);
}
functionswapB(address _vaultProxy,
bytescalldata _callArgs,
bytescalldata _assetTransferArgs
) externalfundAssetsTransferHandler(_vaultProxy, _assetTransferArgs) {
__decodeCallArgsAndSwap(_callArgs);
}
functionswapDirectFromVault(address _vaultProxy,
bytescalldata _callArgs,
bytescalldata) external{
(
address[] memory spendAssets,
,
uint256[] memory actualSpendAssetAmounts,
address[] memory incomingAssets,
,
uint256[] memory actualIncomingAssetAmounts
) = __decodeCallArgs(_callArgs);
IMockGenericIntegratee(INTEGRATEE).swapOnBehalf(
payable(_vaultProxy),
spendAssets,
actualSpendAssetAmounts,
incomingAssets,
actualIncomingAssetAmounts
);
}
functionswapViaApproval(address _vaultProxy,
bytescalldata _callArgs,
bytescalldata _assetTransferArgs
) externalfundAssetsTransferHandler(_vaultProxy, _assetTransferArgs) {
__decodeCallArgsAndSwap(_callArgs);
}
function__decodeCallArgs(bytesmemory _callArgs)
internalpurereturns (address[] memory spendAssets_,
uint256[] memory maxSpendAssetAmounts_,
uint256[] memory actualSpendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_,
uint256[] memory actualIncomingAssetAmounts_
)
{
returnabi.decode(
_callArgs,
(address[], uint256[], uint256[], address[], uint256[], uint256[])
);
}
function__decodeCallArgsAndSwap(bytesmemory _callArgs) internal{
(
address[] memory spendAssets,
,
uint256[] memory actualSpendAssetAmounts,
address[] memory incomingAssets,
,
uint256[] memory actualIncomingAssetAmounts
) = __decodeCallArgs(_callArgs);
for (uint256 i; i < spendAssets.length; i++) {
ERC20(spendAssets[i]).approve(INTEGRATEE, actualSpendAssetAmounts[i]);
}
IMockGenericIntegratee(INTEGRATEE).swap(
spendAssets,
actualSpendAssetAmounts,
incomingAssets,
actualIncomingAssetAmounts
);
}
}
Contract Source Code
File 94 of 138: MockGenericIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/SwapperBase.sol";
contractMockGenericIntegrateeisSwapperBase{
functionswap(address[] calldata _assetsToIntegratee,
uint256[] calldata _assetsToIntegrateeAmounts,
address[] calldata _assetsFromIntegratee,
uint256[] calldata _assetsFromIntegrateeAmounts
) externalpayable{
__swap(
msg.sender,
_assetsToIntegratee,
_assetsToIntegrateeAmounts,
_assetsFromIntegratee,
_assetsFromIntegrateeAmounts
);
}
functionswapOnBehalf(addresspayable _trader,
address[] calldata _assetsToIntegratee,
uint256[] calldata _assetsToIntegrateeAmounts,
address[] calldata _assetsFromIntegratee,
uint256[] calldata _assetsFromIntegrateeAmounts
) externalpayable{
__swap(
_trader,
_assetsToIntegratee,
_assetsToIntegrateeAmounts,
_assetsFromIntegratee,
_assetsFromIntegrateeAmounts
);
}
}
Contract Source Code
File 95 of 138: MockIntegrateeBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/NormalizedRateProviderBase.sol";
import"../../utils/SwapperBase.sol";
abstractcontractMockIntegrateeBaseisNormalizedRateProviderBase, SwapperBase{
constructor(address[] memory _defaultRateAssets,
address[] memory _specialAssets,
uint8[] memory _specialAssetDecimals,
uint256 _ratePrecision
)
publicNormalizedRateProviderBase(
_defaultRateAssets,
_specialAssets,
_specialAssetDecimals,
_ratePrecision
)
{}
function__getRate(address _baseAsset, address _quoteAsset)
internalviewoverridereturns (uint256)
{
// 1. Return constant if base asset is quote assetif (_baseAsset == _quoteAsset) {
return10**RATE_PRECISION;
}
// 2. Check for a direct rateuint256 directRate = assetToAssetRate[_baseAsset][_quoteAsset];
if (directRate >0) {
return directRate;
}
// 3. Check for inverse direct rateuint256 iDirectRate = assetToAssetRate[_quoteAsset][_baseAsset];
if (iDirectRate >0) {
return10**(RATE_PRECISION.mul(2)).div(iDirectRate);
}
// 4. Else return 1return10**RATE_PRECISION;
}
}
Contract Source Code
File 96 of 138: MockKyberIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../release/utils/MathHelpers.sol";
import"../prices/CentralizedRateProvider.sol";
import"../utils/SwapperBase.sol";
contractMockKyberIntegrateeisSwapperBase, Ownable, MathHelpers{
usingSafeMathforuint256;
addressprivateimmutable MOCK_CENTRALIZED_RATE_PROVIDER;
addressprivateimmutable WETH;
uint256privateconstant PRECISION =18;
// Deviation set in % defines the MAX deviation per block from the mean rateuint256private blockNumberDeviation;
constructor(address _mockCentralizedRateProvider,
address _weth,
uint256 _blockNumberDeviation
) public{
MOCK_CENTRALIZED_RATE_PROVIDER = _mockCentralizedRateProvider;
WETH = _weth;
blockNumberDeviation = _blockNumberDeviation;
}
functionswapEtherToToken(address _destToken, uint256) externalpayablereturns (uint256) {
uint256 destAmount = CentralizedRateProvider(MOCK_CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomized(WETH, msg.value, _destToken, blockNumberDeviation);
__swapAssets(msg.sender, ETH_ADDRESS, msg.value, _destToken, destAmount);
returnmsg.value;
}
functionswapTokenToEther(address _srcToken,
uint256 _srcAmount,
uint256) externalreturns (uint256) {
uint256 destAmount = CentralizedRateProvider(MOCK_CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomized(_srcToken, _srcAmount, WETH, blockNumberDeviation);
__swapAssets(msg.sender, _srcToken, _srcAmount, ETH_ADDRESS, destAmount);
return _srcAmount;
}
functionswapTokenToToken(address _srcToken,
uint256 _srcAmount,
address _destToken,
uint256) externalreturns (uint256) {
uint256 destAmount = CentralizedRateProvider(MOCK_CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomized(_srcToken, _srcAmount, _destToken, blockNumberDeviation);
__swapAssets(msg.sender, _srcToken, _srcAmount, _destToken, destAmount);
return _srcAmount;
}
functionsetBlockNumberDeviation(uint256 _deviationPct) externalonlyOwner{
blockNumberDeviation = _deviationPct;
}
functiongetExpectedRate(address _srcToken,
address _destToken,
uint256 _amount
) externalreturns (uint256 rate_, uint256 worstRate_) {
if (_srcToken == ETH_ADDRESS) {
_srcToken = WETH;
}
if (_destToken == ETH_ADDRESS) {
_destToken = WETH;
}
uint256 destAmount = CentralizedRateProvider(MOCK_CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomizedBySender(_srcToken, _amount, _destToken);
rate_ = __calcNormalizedRate(
ERC20(_srcToken).decimals(),
_amount,
ERC20(_destToken).decimals(),
destAmount
);
worstRate_ = rate_.mul(uint256(100).sub(blockNumberDeviation)).div(100);
}
///////////////////// STATE GETTERS /////////////////////functiongetCentralizedRateProvider() publicviewreturns (address) {
return MOCK_CENTRALIZED_RATE_PROVIDER;
}
functiongetWeth() publicviewreturns (address) {
return WETH;
}
functiongetBlockNumberDeviation() publicviewreturns (uint256) {
return blockNumberDeviation;
}
functiongetPrecision() publicpurereturns (uint256) {
return PRECISION;
}
}
Contract Source Code
File 97 of 138: MockParaSwapIntegratee.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../release/interfaces/IParaSwapAugustusSwapper.sol";
import"../prices/CentralizedRateProvider.sol";
import"../utils/SwapperBase.sol";
contractMockParaSwapIntegrateeisSwapperBase{
usingSafeMathforuint256;
addressprivateimmutable MOCK_CENTRALIZED_RATE_PROVIDER;
// Deviation set in % defines the MAX deviation per block from the mean rateuint256private blockNumberDeviation;
constructor(address _mockCentralizedRateProvider, uint256 _blockNumberDeviation) public{
MOCK_CENTRALIZED_RATE_PROVIDER = _mockCentralizedRateProvider;
blockNumberDeviation = _blockNumberDeviation;
}
/// @dev Must be `public` to avoid errorfunctionmultiSwap(address _fromToken,
address _toToken,
uint256 _fromAmount,
uint256, // toAmount (min received amount)uint256, // expectedAmount
IParaSwapAugustusSwapper.Path[] memory _paths,
uint256, // mintPriceaddress, // beneficiaryuint256, // donationPercentagestringmemory// referrer) publicpayablereturns (uint256) {
return __multiSwap(_fromToken, _toToken, _fromAmount, _paths);
}
/// @dev Helper to parse the total amount of network fees (in ETH) for the multiSwap() callfunction__calcTotalNetworkFees(IParaSwapAugustusSwapper.Path[] memory _paths)
privatepurereturns (uint256 totalNetworkFees_)
{
for (uint256 i; i < _paths.length; i++) {
totalNetworkFees_ = totalNetworkFees_.add(_paths[i].totalNetworkFee);
}
return totalNetworkFees_;
}
/// @dev Helper to avoid the stack-too-deep errorfunction__multiSwap(address _fromToken,
address _toToken,
uint256 _fromAmount,
IParaSwapAugustusSwapper.Path[] memory _paths
) privatereturns (uint256) {
address[] memory assetsFromIntegratee =newaddress[](1);
assetsFromIntegratee[0] = _toToken;
uint256[] memory assetsFromIntegrateeAmounts =newuint256[](1);
assetsFromIntegrateeAmounts[0] = CentralizedRateProvider(MOCK_CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomized(_fromToken, _fromAmount, _toToken, blockNumberDeviation);
uint256 totalNetworkFees = __calcTotalNetworkFees(_paths);
address[] memory assetsToIntegratee;
uint256[] memory assetsToIntegrateeAmounts;
if (totalNetworkFees >0) {
assetsToIntegratee =newaddress[](2);
assetsToIntegratee[1] = ETH_ADDRESS;
assetsToIntegrateeAmounts =newuint256[](2);
assetsToIntegrateeAmounts[1] = totalNetworkFees;
} else {
assetsToIntegratee =newaddress[](1);
assetsToIntegrateeAmounts =newuint256[](1);
}
assetsToIntegratee[0] = _fromToken;
assetsToIntegrateeAmounts[0] = _fromAmount;
__swap(
msg.sender,
assetsToIntegratee,
assetsToIntegrateeAmounts,
assetsFromIntegratee,
assetsFromIntegrateeAmounts
);
return assetsFromIntegrateeAmounts[0];
}
///////////////////// STATE GETTERS /////////////////////functiongetBlockNumberDeviation() externalviewreturns (uint256 blockNumberDeviation_) {
return blockNumberDeviation;
}
functiongetCentralizedRateProvider()
externalviewreturns (address centralizedRateProvider_)
{
return MOCK_CENTRALIZED_RATE_PROVIDER;
}
}
Contract Source Code
File 98 of 138: MockReentrancyToken.sol
// 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.
*/pragmasolidity 0.6.12;import"../../release/core/fund/comptroller/ComptrollerLib.sol";
import"./MockToken.sol";
/// @title MockReentrancyToken Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mock ERC20 token implementation that is able to re-entrance redeemShares and buyShares functionscontractMockReentrancyTokenisMockToken("Mock Reentrancy Token", "MRT", 18) {
boolpublic bad;
addresspublic comptrollerProxy;
functionmakeItReentracyToken(address _comptrollerProxy) external{
bad =true;
comptrollerProxy = _comptrollerProxy;
}
functiontransfer(address recipient, uint256 amount) publicoverridereturns (bool) {
if (bad) {
ComptrollerLib(comptrollerProxy).redeemShares();
} else {
_transfer(_msgSender(), recipient, amount);
}
returntrue;
}
functiontransferFrom(address sender,
address recipient,
uint256 amount
) publicoverridereturns (bool) {
if (bad) {
ComptrollerLib(comptrollerProxy).buyShares(
newaddress[](0),
newuint256[](0),
newuint256[](0)
);
} else {
_transfer(sender, recipient, amount);
}
returntrue;
}
}
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"./../../release/interfaces/ISynthetixExchangeRates.sol";
import"../prices/MockChainlinkPriceSource.sol";
/// @dev This price source offers two different options getting prices/// The first one is getting a fixed rate, which can be useful for tests/// The second approach calculates dinamically the rate making use of a chainlink price source/// Mocks the functionality of the folllowing Synthetix contracts: { Exchanger, ExchangeRates }contractMockSynthetixPriceSourceisOwnable, ISynthetixExchangeRates{
usingSafeMathforuint256;
mapping(bytes32=>uint256) private fixedRate;
mapping(bytes32=> AggregatorInfo) private currencyKeyToAggregator;
enumRateAsset {ETH, USD}
structAggregatorInfo {
address aggregator;
RateAsset rateAsset;
}
constructor(address _ethUsdAggregator) public{
currencyKeyToAggregator[bytes32("ETH")] = AggregatorInfo({
aggregator: _ethUsdAggregator,
rateAsset: RateAsset.USD
});
}
functionsetPriceSourcesForCurrencyKeys(bytes32[] calldata _currencyKeys,
address[] calldata _aggregators,
RateAsset[] calldata _rateAssets
) externalonlyOwner{
require(
_currencyKeys.length== _aggregators.length&&
_rateAssets.length== _aggregators.length
);
for (uint256 i =0; i < _currencyKeys.length; i++) {
currencyKeyToAggregator[_currencyKeys[i]] = AggregatorInfo({
aggregator: _aggregators[i],
rateAsset: _rateAssets[i]
});
}
}
functionsetRate(bytes32 _currencyKey, uint256 _rate) externalonlyOwner{
fixedRate[_currencyKey] = _rate;
}
/// @dev Calculates the rate from a currencty key against its RateAsset/// TODO: Use CentralizedRateProvider to randomize and make consistent with the restfunctionrateAndInvalid(bytes32 _currencyKey)
externalviewoverridereturns (uint256 rate_, bool isInvalid_)
{
uint256 storedRate = getFixedRate(_currencyKey);
if (storedRate !=0) {
rate_ = storedRate;
} else {
AggregatorInfo memory aggregatorInfo = getAggregatorFromCurrencyKey(_currencyKey);
address aggregator = aggregatorInfo.aggregator;
if (aggregator !=address(0)) {
uint256 decimals = MockChainlinkPriceSource(aggregator).decimals();
rate_ =uint256(MockChainlinkPriceSource(aggregator).latestAnswer()).mul(
10**(uint256(18).sub(decimals))
);
if (aggregatorInfo.rateAsset == RateAsset.ETH) {
uint256 ethToUsd =uint256(
MockChainlinkPriceSource(
getAggregatorFromCurrencyKey(bytes32("ETH"))
.aggregator
)
.latestAnswer()
);
rate_ = rate_.mul(ethToUsd).div(10**8);
}
}
}
isInvalid_ = (rate_ ==0);
return (rate_, isInvalid_);
}
///////////////////// STATE GETTERS /////////////////////functiongetAggregatorFromCurrencyKey(bytes32 _currencyKey)
publicviewreturns (AggregatorInfo memory _aggregator)
{
return currencyKeyToAggregator[_currencyKey];
}
functiongetFixedRate(bytes32 _currencyKey) publicviewreturns (uint256) {
return fixedRate[_currencyKey];
}
}
Contract Source Code
File 101 of 138: MockSynthetixToken.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"./../../release/interfaces/ISynthetixProxyERC20.sol";
import"./../../release/interfaces/ISynthetixSynth.sol";
import"./MockToken.sol";
contractMockSynthetixTokenisISynthetixProxyERC20, ISynthetixSynth, MockToken, Ownable{
usingSafeMathforuint256;
bytes32publicoverride currencyKey;
uint256publicconstant WAITING_PERIOD_SECS =3*60;
mapping(address=>uint256) public timelockByAccount;
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
bytes32 _currencyKey
) publicMockToken(_name, _symbol, _decimals) {
currencyKey = _currencyKey;
}
functionsetCurrencyKey(bytes32 _currencyKey) externalonlyOwner{
currencyKey = _currencyKey;
}
function_isLocked(address account) internalviewreturns (bool) {
return timelockByAccount[account] >=now;
}
function_beforeTokenTransfer(addressfrom,
address,
uint256) internaloverride{
require(!_isLocked(from), "Cannot settle during waiting period");
}
functiontarget() externalviewoverridereturns (address) {
returnaddress(this);
}
functionisLocked(address account) externalviewreturns (bool) {
return _isLocked(account);
}
functionburnFrom(address account, uint256 amount) publicoverride{
_burn(account, amount);
}
functionlock(address account) public{
timelockByAccount[account] =now.add(WAITING_PERIOD_SECS);
}
functionunlock(address account) public{
timelockByAccount[account] =0;
}
}
Contract Source Code
File 102 of 138: MockToken.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
contractMockTokenisERC20Burnable{
usingSafeMathforuint256;
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) publicERC20(_name, _symbol) {
_setupDecimals(_decimals);
_mint(msg.sender, uint256(100000000).mul(10**uint256(_decimals)));
}
functionmintFor(address _who, uint256 _amount) external{
_mint(_who, _amount);
}
functionmint(uint256 _amount) external{
_mint(msg.sender, _amount);
}
}
Contract Source Code
File 103 of 138: MockUniswapV2Integratee.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"../prices/CentralizedRateProvider.sol";
import"./utils/SimpleMockIntegrateeBase.sol";
/// @dev Mocks the integration with `UniswapV2Router02` <https://uniswap.org/docs/v2/smart-contracts/router02/>/// Additionally mocks the integration with `UniswapV2Factory` <https://uniswap.org/docs/v2/smart-contracts/factory/>contractMockUniswapV2IntegrateeisSwapperBase, Ownable{
usingSafeMathforuint256;
mapping(address=>mapping(address=>address)) private assetToAssetToPair;
addressprivateimmutable CENTRALIZED_RATE_PROVIDER;
uint256privateconstant PRECISION =18;
// Set in %, defines the MAX deviation per block from the mean rateuint256private blockNumberDeviation;
constructor(address[] memory _listOfToken0,
address[] memory _listOfToken1,
address[] memory _listOfPair,
address _centralizedRateProvider,
uint256 _blockNumberDeviation
) public{
addPair(_listOfToken0, _listOfToken1, _listOfPair);
CENTRALIZED_RATE_PROVIDER = _centralizedRateProvider;
blockNumberDeviation = _blockNumberDeviation;
}
/// @dev Adds the maximum possible value from {_amountADesired _amountBDesired}/// Makes use of the value interpreter to perform those calculationsfunctionaddLiquidity(address _tokenA,
address _tokenB,
uint256 _amountADesired,
uint256 _amountBDesired,
uint256,
uint256,
address,
uint256)
externalreturns (uint256,
uint256,
uint256)
{
__addLiquidity(_tokenA, _tokenB, _amountADesired, _amountBDesired);
}
/// @dev Removes the specified amount of liquidity/// Returns 50% of the incoming liquidity value on each token.functionremoveLiquidity(address _tokenA,
address _tokenB,
uint256 _liquidity,
uint256,
uint256,
address,
uint256) publicreturns (uint256, uint256) {
__removeLiquidity(_tokenA, _tokenB, _liquidity);
}
functionswapExactTokensForTokens(uint256 amountIn,
uint256,
address[] calldata path,
address,
uint256) externalreturns (uint256[] memory) {
uint256 amountOut = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomized(path[0], amountIn, path[1], blockNumberDeviation);
__swapAssets(msg.sender, path[0], amountIn, path[path.length-1], amountOut);
}
/// @dev We don't calculate any intermediate values here because they aren't actually used/// Returns the randomized by sender value of the edge path assetsfunctiongetAmountsOut(uint256 _amountIn, address[] calldata _path)
externalreturns (uint256[] memory amounts_)
{
require(_path.length>=2, "getAmountsOut: path must be >= 2");
address assetIn = _path[0];
address assetOut = _path[_path.length-1];
uint256 amountOut = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValueRandomizedBySender(assetIn, _amountIn, assetOut);
amounts_ =newuint256[](_path.length);
amounts_[0] = _amountIn;
amounts_[_path.length-1] = amountOut;
return amounts_;
}
functionaddPair(address[] memory _listOfToken0,
address[] memory _listOfToken1,
address[] memory _listOfPair
) publiconlyOwner{
require(
_listOfPair.length== _listOfToken0.length,
"constructor: _listOfPair and _listOfToken0 have an unequal length"
);
require(
_listOfPair.length== _listOfToken1.length,
"constructor: _listOfPair and _listOfToken1 have an unequal length"
);
for (uint256 i; i < _listOfPair.length; i++) {
address token0 = _listOfToken0[i];
address token1 = _listOfToken1[i];
address pair = _listOfPair[i];
assetToAssetToPair[token0][token1] = pair;
assetToAssetToPair[token1][token0] = pair;
}
}
functionsetBlockNumberDeviation(uint256 _deviationPct) externalonlyOwner{
blockNumberDeviation = _deviationPct;
}
// PRIVATE FUNCTIONS/// Avoids stack-too-deep error.function__addLiquidity(address _tokenA,
address _tokenB,
uint256 _amountADesired,
uint256 _amountBDesired
) private{
address pair = getPair(_tokenA, _tokenB);
uint256 amountA;
uint256 amountB;
uint256 amountBFromA = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(_tokenA, _amountADesired, _tokenB);
uint256 amountAFromB = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(_tokenB, _amountBDesired, _tokenA);
if (amountBFromA >= _amountBDesired) {
amountA = amountAFromB;
amountB = _amountBDesired;
} else {
amountA = _amountADesired;
amountB = amountBFromA;
}
uint256 tokenPerLPToken = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(pair, 10**uint256(PRECISION), _tokenA);
// Calculate the inverse rate to know the amount of LPToken to return from a unit of tokenuint256 inverseRate =uint256(10**PRECISION).mul(10**PRECISION).div(tokenPerLPToken);
// Total liquidity can be calculated as 2x liquidity from amount Auint256 totalLiquidity =uint256(2).mul(
amountA.mul(inverseRate).div(uint256(10**PRECISION))
);
require(
ERC20(pair).balanceOf(address(this)) >= totalLiquidity,
"__addLiquidity: Integratee doesn't have enough pair balance to cover the expected amount"
);
address[] memory assetsToIntegratee =newaddress[](2);
uint256[] memory assetsToIntegrateeAmounts =newuint256[](2);
address[] memory assetsFromIntegratee =newaddress[](1);
uint256[] memory assetsFromIntegrateeAmounts =newuint256[](1);
assetsToIntegratee[0] = _tokenA;
assetsToIntegrateeAmounts[0] = amountA;
assetsToIntegratee[1] = _tokenB;
assetsToIntegrateeAmounts[1] = amountB;
assetsFromIntegratee[0] = pair;
assetsFromIntegrateeAmounts[0] = totalLiquidity;
__swap(
msg.sender,
assetsToIntegratee,
assetsToIntegrateeAmounts,
assetsFromIntegratee,
assetsFromIntegrateeAmounts
);
}
/// Avoids stack-too-deep error.function__removeLiquidity(address _tokenA,
address _tokenB,
uint256 _liquidity
) private{
address pair = assetToAssetToPair[_tokenA][_tokenB];
require(pair !=address(0), "__removeLiquidity: this pair doesn't exist");
uint256 amountA = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(pair, _liquidity, _tokenA)
.div(uint256(2));
uint256 amountB = CentralizedRateProvider(CENTRALIZED_RATE_PROVIDER)
.calcLiveAssetValue(pair, _liquidity, _tokenB)
.div(uint256(2));
address[] memory assetsToIntegratee =newaddress[](1);
uint256[] memory assetsToIntegrateeAmounts =newuint256[](1);
address[] memory assetsFromIntegratee =newaddress[](2);
uint256[] memory assetsFromIntegrateeAmounts =newuint256[](2);
assetsToIntegratee[0] = pair;
assetsToIntegrateeAmounts[0] = _liquidity;
assetsFromIntegratee[0] = _tokenA;
assetsFromIntegrateeAmounts[0] = amountA;
assetsFromIntegratee[1] = _tokenB;
assetsFromIntegrateeAmounts[1] = amountB;
require(
ERC20(_tokenA).balanceOf(address(this)) >= amountA,
"__removeLiquidity: Integratee doesn't have enough tokenA balance to cover the expected amount"
);
require(
ERC20(_tokenB).balanceOf(address(this)) >= amountA,
"__removeLiquidity: Integratee doesn't have enough tokenB balance to cover the expected amount"
);
__swap(
msg.sender,
assetsToIntegratee,
assetsToIntegrateeAmounts,
assetsFromIntegratee,
assetsFromIntegrateeAmounts
);
}
///////////////////// STATE GETTERS //////////////////////// @dev By default set to address(0). It is read by UniswapV2PoolTokenValueCalculator: __calcPoolTokenValuefunctionfeeTo() externalpurereturns (address) {
returnaddress(0);
}
functiongetCentralizedRateProvider() publicviewreturns (address) {
return CENTRALIZED_RATE_PROVIDER;
}
functiongetBlockNumberDeviation() publicviewreturns (uint256) {
return blockNumberDeviation;
}
functiongetPrecision() publicpurereturns (uint256) {
return PRECISION;
}
functiongetPair(address _token0, address _token1) publicviewreturns (address) {
return assetToAssetToPair[_token0][_token1];
}
}
Contract Source Code
File 104 of 138: MockUniswapV2PriceSource.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../release/interfaces/IUniswapV2Pair.sol";
import"../tokens/MockToken.sol";
/// @dev This price source mocks the integration with Uniswap Pair/// Docs of Uniswap Pair implementation: <https://uniswap.org/docs/v2/smart-contracts/pair/>contractMockUniswapV2PriceSourceisMockToken("Uniswap V2", "UNI-V2", 18) {
addressprivateimmutable TOKEN_0;
addressprivateimmutable TOKEN_1;
constructor(address _token0, address _token1) public{
TOKEN_0 = _token0;
TOKEN_1 = _token1;
}
/// @dev returns reserves for each token on the Uniswap Pair/// Reserves will be used to calculate the pair price/// Inherited from IUniswapV2PairfunctiongetReserves()
externalviewreturns (uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
)
{
reserve0 =uint112(ERC20(token0()).balanceOf(address(this)));
reserve1 =uint112(ERC20(token1()).balanceOf(address(this)));
return (reserve0, reserve1, uint32(block.timestamp));
}
///////////////////// STATE GETTERS //////////////////////// @dev Inherited from IUniswapV2Pairfunctiontoken0() publicviewreturns (address) {
return TOKEN_0;
}
/// @dev Inherited from IUniswapV2Pairfunctiontoken1() publicviewreturns (address) {
return TOKEN_1;
}
/// @dev Inherited from IUniswapV2PairfunctionkLast() publicpurereturns (uint256) {
return0;
}
}
Contract Source Code
File 105 of 138: MockVaultLib.sol
// 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.
*/pragmasolidity 0.6.12;import"../persistent/vault/VaultLibBaseCore.sol";
/// @title MockVaultLib Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mock VaultLib implementation that only extends VaultLibBaseCorecontractMockVaultLibisVaultLibBaseCore{
functiongetAccessor() externalviewreturns (address) {
return accessor;
}
functiongetCreator() externalviewreturns (address) {
return creator;
}
functiongetMigrator() externalviewreturns (address) {
return migrator;
}
functiongetOwner() externalviewreturns (address) {
return owner;
}
}
Contract Source Code
File 106 of 138: NormalizedRateProviderBase.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"./RateProviderBase.sol";
abstractcontractNormalizedRateProviderBaseisRateProviderBase{
usingSafeMathforuint256;
uint256publicimmutable RATE_PRECISION;
constructor(address[] memory _defaultRateAssets,
address[] memory _specialAssets,
uint8[] memory _specialAssetDecimals,
uint256 _ratePrecision
) publicRateProviderBase(_specialAssets, _specialAssetDecimals) {
RATE_PRECISION = _ratePrecision;
for (uint256 i =0; i < _defaultRateAssets.length; i++) {
for (uint256 j = i +1; j < _defaultRateAssets.length; j++) {
assetToAssetRate[_defaultRateAssets[i]][_defaultRateAssets[j]] =10**_ratePrecision;
assetToAssetRate[_defaultRateAssets[j]][_defaultRateAssets[i]] =10**_ratePrecision;
}
}
}
// TODO: move to main contracts' utils for use with pricesfunction__calcDenormalizedQuoteAssetAmount(uint256 _baseAssetDecimals,
uint256 _baseAssetAmount,
uint256 _quoteAssetDecimals,
uint256 _rate
) internalviewreturns (uint256) {
return
_rate.mul(_baseAssetAmount).mul(10**_quoteAssetDecimals).div(
10**(RATE_PRECISION.add(_baseAssetDecimals))
);
}
}
Contract Source Code
File 107 of 138: Ownable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;import"../GSN/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/contractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor () internal{
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
emit OwnershipTransferred(_owner, address(0));
_owner =address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
Contract Source Code
File 108 of 138: ParaSwapAdapter.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/IParaSwapAugustusSwapper.sol";
import"../../../../interfaces/IWETH.sol";
import"../utils/AdapterBase.sol";
/// @title ParaSwapAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for interacting with ParaSwapcontractParaSwapAdapterisAdapterBase{
usingSafeMathforuint256;
stringprivateconstant REFERRER ="enzyme";
addressprivateimmutable EXCHANGE;
addressprivateimmutable TOKEN_TRANSFER_PROXY;
addressprivateimmutable WETH_TOKEN;
constructor(address _integrationManager,
address _exchange,
address _tokenTransferProxy,
address _wethToken
) publicAdapterBase(_integrationManager) {
EXCHANGE = _exchange;
TOKEN_TRANSFER_PROXY = _tokenTransferProxy;
WETH_TOKEN = _wethToken;
}
/// @dev Needed to receive ETH refund from sent network feesreceive() externalpayable{}
// EXTERNAL FUNCTIONS/// @notice Provides a constant string identifier for an adapter/// @return identifier_ An identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"PARASWAP";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
require(_selector == TAKE_ORDER_SELECTOR, "parseAssetsForMethod: _selector invalid");
(
address incomingAsset,
uint256 minIncomingAssetAmount,
,
address outgoingAsset,
uint256 outgoingAssetAmount,
IParaSwapAugustusSwapper.Path[] memory paths
) = __decodeCallArgs(_encodedCallArgs);
// Format incoming assets
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = incomingAsset;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minIncomingAssetAmount;
// Format outgoing assets depending on if there are network feesuint256 totalNetworkFees = __calcTotalNetworkFees(paths);
if (totalNetworkFees >0) {
// We are not performing special logic if the incomingAsset is the fee assetif (outgoingAsset == WETH_TOKEN) {
spendAssets_ =newaddress[](1);
spendAssets_[0] = outgoingAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount.add(totalNetworkFees);
} else {
spendAssets_ =newaddress[](2);
spendAssets_[0] = outgoingAsset;
spendAssets_[1] = WETH_TOKEN;
spendAssetAmounts_ =newuint256[](2);
spendAssetAmounts_[0] = outgoingAssetAmount;
spendAssetAmounts_[1] = totalNetworkFees;
}
} else {
spendAssets_ =newaddress[](1);
spendAssets_[0] = outgoingAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount;
}
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Trades assets on ParaSwap/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctiontakeOrder(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
__takeOrder(_vaultProxy, _encodedCallArgs);
}
// PRIVATE FUNCTIONS/// @dev Helper to parse the total amount of network fees (in ETH) for the multiSwap() callfunction__calcTotalNetworkFees(IParaSwapAugustusSwapper.Path[] memory _paths)
privatepurereturns (uint256 totalNetworkFees_)
{
for (uint256 i; i < _paths.length; i++) {
totalNetworkFees_ = totalNetworkFees_.add(_paths[i].totalNetworkFee);
}
return totalNetworkFees_;
}
/// @dev Helper to decode the encoded callOnIntegration call argumentsfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address incomingAsset_,
uint256 minIncomingAssetAmount_,
uint256 expectedIncomingAssetAmount_, // Passed as a courtesy to ParaSwap for analyticsaddress outgoingAsset_,
uint256 outgoingAssetAmount_,
IParaSwapAugustusSwapper.Path[] memory paths_
)
{
returnabi.decode(
_encodedCallArgs,
(address, uint256, uint256, address, uint256, IParaSwapAugustusSwapper.Path[])
);
}
/// @dev Helper to encode the call to ParaSwap multiSwap() as low-level calldata./// Avoids the stack-too-deep error.function__encodeMultiSwapCallData(address _vaultProxy,
address _incomingAsset,
uint256 _minIncomingAssetAmount,
uint256 _expectedIncomingAssetAmount, // Passed as a courtesy to ParaSwap for analyticsaddress _outgoingAsset,
uint256 _outgoingAssetAmount,
IParaSwapAugustusSwapper.Path[] memory _paths
) privatepurereturns (bytesmemory multiSwapCallData) {
returnabi.encodeWithSelector(
IParaSwapAugustusSwapper.multiSwap.selector,
_outgoingAsset, // fromToken
_incomingAsset, // toToken
_outgoingAssetAmount, // fromAmount
_minIncomingAssetAmount, // toAmount
_expectedIncomingAssetAmount, // expectedAmount
_paths, // path0, // mintPricepayable(_vaultProxy), // beneficiary0, // donationPercentage
REFERRER // referrer
);
}
/// @dev Helper to execute ParaSwapAugustusSwapper.multiSwap() via a low-level call./// Avoids the stack-too-deep error.function__executeMultiSwap(bytesmemory _multiSwapCallData, uint256 _totalNetworkFees)
private{
(bool success, bytesmemory returnData) = EXCHANGE.call{value: _totalNetworkFees}(
_multiSwapCallData
);
require(success, string(returnData));
}
/// @dev Helper for the inner takeOrder() logic./// Avoids the stack-too-deep error.function__takeOrder(address _vaultProxy, bytesmemory _encodedCallArgs) private{
(
address incomingAsset,
uint256 minIncomingAssetAmount,
uint256 expectedIncomingAssetAmount,
address outgoingAsset,
uint256 outgoingAssetAmount,
IParaSwapAugustusSwapper.Path[] memory paths
) = __decodeCallArgs(_encodedCallArgs);
__approveMaxAsNeeded(outgoingAsset, TOKEN_TRANSFER_PROXY, outgoingAssetAmount);
// If there are network fees, unwrap enough WETH to cover the feesuint256 totalNetworkFees = __calcTotalNetworkFees(paths);
if (totalNetworkFees >0) {
__unwrapWeth(totalNetworkFees);
}
// Get the callData for the low-level multiSwap() callbytesmemory multiSwapCallData = __encodeMultiSwapCallData(
_vaultProxy,
incomingAsset,
minIncomingAssetAmount,
expectedIncomingAssetAmount,
outgoingAsset,
outgoingAssetAmount,
paths
);
// Execute the trade on ParaSwap
__executeMultiSwap(multiSwapCallData, totalNetworkFees);
// If fees were paid and ETH remains in the contract, wrap it as WETH so it can be returnedif (totalNetworkFees >0) {
__wrapEth();
}
}
/// @dev Helper to unwrap specified amount of WETH into ETH./// Avoids the stack-too-deep error.function__unwrapWeth(uint256 _amount) private{
IWETH(payable(WETH_TOKEN)).withdraw(_amount);
}
/// @dev Helper to wrap all ETH in contract as WETH./// Avoids the stack-too-deep error.function__wrapEth() private{
uint256 ethBalance =payable(address(this)).balance;
if (ethBalance >0) {
IWETH(payable(WETH_TOKEN)).deposit{value: ethBalance}();
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `EXCHANGE` variable/// @return exchange_ The `EXCHANGE` variable valuefunctiongetExchange() externalviewreturns (address exchange_) {
return EXCHANGE;
}
/// @notice Gets the `TOKEN_TRANSFER_PROXY` variable/// @return tokenTransferProxy_ The `TOKEN_TRANSFER_PROXY` variable valuefunctiongetTokenTransferProxy() externalviewreturns (address tokenTransferProxy_) {
return TOKEN_TRANSFER_PROXY;
}
/// @notice Gets the `WETH_TOKEN` variable/// @return wethToken_ The `WETH_TOKEN` variable valuefunctiongetWethToken() externalviewreturns (address wethToken_) {
return WETH_TOKEN;
}
}
Contract Source Code
File 109 of 138: PerformanceFee.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/math/SignedSafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../../core/fund/comptroller/ComptrollerLib.sol";
import"../FeeManager.sol";
import"./utils/FeeBase.sol";
/// @title PerformanceFee Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A performance-based fee with configurable rate and crystallization period, using/// a high watermark/// @dev This contract assumes that all shares in the VaultProxy are shares outstanding,/// which is fine for this release. Even if they are not, they are still shares that/// are only claimable by the fund owner.contractPerformanceFeeisFeeBase{
usingSafeMathforuint256;
usingSignedSafeMathforint256;
eventActivatedForFund(addressindexed comptrollerProxy, uint256 highWaterMark);
eventFundSettingsAdded(addressindexed comptrollerProxy, uint256 rate, uint256 period);
eventLastSharePriceUpdated(addressindexed comptrollerProxy,
uint256 prevSharePrice,
uint256 nextSharePrice
);
eventPaidOut(addressindexed comptrollerProxy,
uint256 prevHighWaterMark,
uint256 nextHighWaterMark,
uint256 aggregateValueDue
);
eventPerformanceUpdated(addressindexed comptrollerProxy,
uint256 prevAggregateValueDue,
uint256 nextAggregateValueDue,
int256 sharesOutstandingDiff
);
structFeeInfo {
uint256 rate;
uint256 period;
uint256 activated;
uint256 lastPaid;
uint256 highWaterMark;
uint256 lastSharePrice;
uint256 aggregateValueDue;
}
uint256privateconstant RATE_DIVISOR =10**18;
uint256privateconstant SHARE_UNIT =10**18;
mapping(address=> FeeInfo) private comptrollerProxyToFeeInfo;
constructor(address _feeManager) publicFeeBase(_feeManager) {}
// EXTERNAL FUNCTIONS/// @notice Activates the fee for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fundfunctionactivateForFund(address _comptrollerProxy, address) externaloverrideonlyFeeManager{
FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy];
// We must not force asset finality, otherwise funds that have Synths as tracked assets// would be susceptible to a DoS attack when attempting to migrate to a release that uses// this fee: an attacker trades a negligible amount of a tracked Synth with the VaultProxy// as the recipient, thus causing `calcGrossShareValue(true)` to fail.
(uint256 grossSharePrice, bool sharePriceIsValid) = ComptrollerLib(_comptrollerProxy)
.calcGrossShareValue(false);
require(sharePriceIsValid, "activateForFund: Invalid share price");
feeInfo.highWaterMark = grossSharePrice;
feeInfo.lastSharePrice = grossSharePrice;
feeInfo.activated =block.timestamp;
emit ActivatedForFund(_comptrollerProxy, grossSharePrice);
}
/// @notice Add the initial fee settings for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _settingsData Encoded settings to apply to the policy for the fund/// @dev `highWaterMark`, `lastSharePrice`, and `activated` are set during activationfunctionaddFundSettings(address _comptrollerProxy, bytescalldata _settingsData)
externaloverrideonlyFeeManager{
(uint256 feeRate, uint256 feePeriod) =abi.decode(_settingsData, (uint256, uint256));
require(feeRate >0, "addFundSettings: feeRate must be greater than 0");
require(feePeriod >0, "addFundSettings: feePeriod must be greater than 0");
comptrollerProxyToFeeInfo[_comptrollerProxy] = FeeInfo({
rate: feeRate,
period: feePeriod,
activated: 0,
lastPaid: 0,
highWaterMark: 0,
lastSharePrice: 0,
aggregateValueDue: 0
});
emit FundSettingsAdded(_comptrollerProxy, feeRate, feePeriod);
}
/// @notice Provides a constant string identifier for a fee/// @return identifier_ The identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"PERFORMANCE";
}
/// @notice Gets the hooks that are implemented by the fee/// @return implementedHooksForSettle_ The hooks during which settle() is implemented/// @return implementedHooksForUpdate_ The hooks during which update() is implemented/// @return usesGavOnSettle_ True if GAV is used during the settle() implementation/// @return usesGavOnUpdate_ True if GAV is used during the update() implementation/// @dev Used only during fee registrationfunctionimplementedHooks()
externalviewoverridereturns (
IFeeManager.FeeHook[] memory implementedHooksForSettle_,
IFeeManager.FeeHook[] memory implementedHooksForUpdate_,
bool usesGavOnSettle_,
bool usesGavOnUpdate_
)
{
implementedHooksForSettle_ =new IFeeManager.FeeHook[](3);
implementedHooksForSettle_[0] = IFeeManager.FeeHook.Continuous;
implementedHooksForSettle_[1] = IFeeManager.FeeHook.BuySharesSetup;
implementedHooksForSettle_[2] = IFeeManager.FeeHook.PreRedeemShares;
implementedHooksForUpdate_ =new IFeeManager.FeeHook[](3);
implementedHooksForUpdate_[0] = IFeeManager.FeeHook.Continuous;
implementedHooksForUpdate_[1] = IFeeManager.FeeHook.BuySharesCompleted;
implementedHooksForUpdate_[2] = IFeeManager.FeeHook.PreRedeemShares;
return (implementedHooksForSettle_, implementedHooksForUpdate_, true, true);
}
/// @notice Checks whether the shares outstanding for the fee can be paid out, and updates/// the info for the fee's last payout/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return isPayable_ True if shares outstanding can be paid outfunctionpayout(address _comptrollerProxy, address)
externaloverrideonlyFeeManagerreturns (bool isPayable_)
{
if (!payoutAllowed(_comptrollerProxy)) {
returnfalse;
}
FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy];
feeInfo.lastPaid =block.timestamp;
uint256 prevHighWaterMark = feeInfo.highWaterMark;
uint256 nextHighWaterMark = __calcUint256Max(feeInfo.lastSharePrice, prevHighWaterMark);
uint256 prevAggregateValueDue = feeInfo.aggregateValueDue;
// Update state as necessaryif (prevAggregateValueDue >0) {
feeInfo.aggregateValueDue =0;
}
if (nextHighWaterMark > prevHighWaterMark) {
feeInfo.highWaterMark = nextHighWaterMark;
}
emit PaidOut(
_comptrollerProxy,
prevHighWaterMark,
nextHighWaterMark,
prevAggregateValueDue
);
returntrue;
}
/// @notice Settles the fee and calculates shares due/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _vaultProxy The VaultProxy of the fund/// @param _gav The GAV of the fund/// @return settlementType_ The type of settlement/// @return (unused) The payer of shares due/// @return sharesDue_ The amount of shares duefunctionsettle(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook,
bytescalldata,
uint256 _gav
)
externaloverrideonlyFeeManagerreturns (
IFeeManager.SettlementType settlementType_,
address,
uint256 sharesDue_
)
{
if (_gav ==0) {
return (IFeeManager.SettlementType.None, address(0), 0);
}
int256 settlementSharesDue = __settleAndUpdatePerformance(
_comptrollerProxy,
_vaultProxy,
_gav
);
if (settlementSharesDue ==0) {
return (IFeeManager.SettlementType.None, address(0), 0);
} elseif (settlementSharesDue >0) {
// Settle by minting shares outstanding for custodyreturn (
IFeeManager.SettlementType.MintSharesOutstanding,
address(0),
uint256(settlementSharesDue)
);
} else {
// Settle by burning from shares outstandingreturn (
IFeeManager.SettlementType.BurnSharesOutstanding,
address(0),
uint256(-settlementSharesDue)
);
}
}
/// @notice Updates the fee state after all fees have finished settle()/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _vaultProxy The VaultProxy of the fund/// @param _hook The FeeHook being executed/// @param _settlementData Encoded args to use in calculating the settlement/// @param _gav The GAV of the fundfunctionupdate(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook _hook,
bytescalldata _settlementData,
uint256 _gav
) externaloverrideonlyFeeManager{
uint256 prevSharePrice = comptrollerProxyToFeeInfo[_comptrollerProxy].lastSharePrice;
uint256 nextSharePrice = __calcNextSharePrice(
_comptrollerProxy,
_vaultProxy,
_hook,
_settlementData,
_gav
);
if (nextSharePrice == prevSharePrice) {
return;
}
comptrollerProxyToFeeInfo[_comptrollerProxy].lastSharePrice = nextSharePrice;
emit LastSharePriceUpdated(_comptrollerProxy, prevSharePrice, nextSharePrice);
}
// PUBLIC FUNCTIONS/// @notice Checks whether the shares outstanding can be paid out/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return payoutAllowed_ True if the fee payment is due/// @dev Payout is allowed if fees have not yet been settled in a crystallization period,/// and at least 1 crystallization period has passed since activationfunctionpayoutAllowed(address _comptrollerProxy) publicviewreturns (bool payoutAllowed_) {
FeeInfo memory feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy];
uint256 period = feeInfo.period;
uint256 timeSinceActivated =block.timestamp.sub(feeInfo.activated);
// Check if at least 1 crystallization period has passed since activationif (timeSinceActivated < period) {
returnfalse;
}
// Check that a full crystallization period has passed since the last payoutuint256 timeSincePeriodStart = timeSinceActivated % period;
uint256 periodStart =block.timestamp.sub(timeSincePeriodStart);
return feeInfo.lastPaid < periodStart;
}
// PRIVATE FUNCTIONS/// @dev Helper to calculate the aggregated value accumulated to a fund since the last/// settlement (happening at investment/redemption)/// Validated:/// _netSharesSupply > 0/// _sharePriceWithoutPerformance != _prevSharePricefunction__calcAggregateValueDue(uint256 _netSharesSupply,
uint256 _sharePriceWithoutPerformance,
uint256 _prevSharePrice,
uint256 _prevAggregateValueDue,
uint256 _feeRate,
uint256 _highWaterMark
) privatepurereturns (uint256) {
int256 superHWMValueSinceLastSettled = (
int256(__calcUint256Max(_highWaterMark, _sharePriceWithoutPerformance)).sub(
int256(__calcUint256Max(_highWaterMark, _prevSharePrice))
)
)
.mul(int256(_netSharesSupply))
.div(int256(SHARE_UNIT));
int256 valueDueSinceLastSettled = superHWMValueSinceLastSettled.mul(int256(_feeRate)).div(
int256(RATE_DIVISOR)
);
returnuint256(
__calcInt256Max(0, int256(_prevAggregateValueDue).add(valueDueSinceLastSettled))
);
}
/// @dev Helper to calculate the max of two int valuesfunction__calcInt256Max(int256 _a, int256 _b) privatepurereturns (int256) {
if (_a >= _b) {
return _a;
}
return _b;
}
/// @dev Helper to calculate the next `lastSharePrice` valuefunction__calcNextSharePrice(address _comptrollerProxy,
address _vaultProxy,
IFeeManager.FeeHook _hook,
bytesmemory _settlementData,
uint256 _gav
) privateviewreturns (uint256 nextSharePrice_) {
uint256 denominationAssetUnit =10**uint256(ERC20(ComptrollerLib(_comptrollerProxy).getDenominationAsset()).decimals());
if (_gav ==0) {
return denominationAssetUnit;
}
// Get shares outstanding via VaultProxy balance and calc shares supply to get net shares supply
ERC20 vaultProxyContract = ERC20(_vaultProxy);
uint256 totalSharesSupply = vaultProxyContract.totalSupply();
uint256 nextNetSharesSupply = totalSharesSupply.sub(
vaultProxyContract.balanceOf(_vaultProxy)
);
if (nextNetSharesSupply ==0) {
return denominationAssetUnit;
}
uint256 nextGav = _gav;
// For both Continuous and BuySharesCompleted hooks, _gav and shares supply will not change,// we only need additional calculations for PreRedeemSharesif (_hook == IFeeManager.FeeHook.PreRedeemShares) {
(, uint256 sharesDecrease) = __decodePreRedeemSharesSettlementData(_settlementData);
// Shares have not yet been burned
nextNetSharesSupply = nextNetSharesSupply.sub(sharesDecrease);
if (nextNetSharesSupply ==0) {
return denominationAssetUnit;
}
// Assets have not yet been withdrawnuint256 gavDecrease = sharesDecrease
.mul(_gav)
.mul(SHARE_UNIT)
.div(totalSharesSupply)
.div(denominationAssetUnit);
nextGav = nextGav.sub(gavDecrease);
if (nextGav ==0) {
return denominationAssetUnit;
}
}
return nextGav.mul(SHARE_UNIT).div(nextNetSharesSupply);
}
/// @dev Helper to calculate the performance metrics for a fund./// Validated:/// _totalSharesSupply > 0/// _gav > 0/// _totalSharesSupply != _totalSharesOutstandingfunction__calcPerformance(address _comptrollerProxy,
uint256 _totalSharesSupply,
uint256 _totalSharesOutstanding,
uint256 _prevAggregateValueDue,
FeeInfo memory feeInfo,
uint256 _gav
) privateviewreturns (uint256 nextAggregateValueDue_, int256 sharesDue_) {
// Use the 'shares supply net shares outstanding' for performance calcs.// Cannot be 0, as _totalSharesSupply != _totalSharesOutstandinguint256 netSharesSupply = _totalSharesSupply.sub(_totalSharesOutstanding);
uint256 sharePriceWithoutPerformance = _gav.mul(SHARE_UNIT).div(netSharesSupply);
// If gross share price has not changed, can exit earlyuint256 prevSharePrice = feeInfo.lastSharePrice;
if (sharePriceWithoutPerformance == prevSharePrice) {
return (_prevAggregateValueDue, 0);
}
nextAggregateValueDue_ = __calcAggregateValueDue(
netSharesSupply,
sharePriceWithoutPerformance,
prevSharePrice,
_prevAggregateValueDue,
feeInfo.rate,
feeInfo.highWaterMark
);
sharesDue_ = __calcSharesDue(
_comptrollerProxy,
netSharesSupply,
_gav,
nextAggregateValueDue_
);
return (nextAggregateValueDue_, sharesDue_);
}
/// @dev Helper to calculate sharesDue during settlement./// Validated:/// _netSharesSupply > 0/// _gav > 0function__calcSharesDue(address _comptrollerProxy,
uint256 _netSharesSupply,
uint256 _gav,
uint256 _nextAggregateValueDue
) privateviewreturns (int256 sharesDue_) {
// If _nextAggregateValueDue > _gav, then no shares can be created.// This is a known limitation of the model, which is only reached for unrealistically// high performance fee rates (> 100%). A revert is allowed in such a case.uint256 sharesDueForAggregateValueDue = _nextAggregateValueDue.mul(_netSharesSupply).div(
_gav.sub(_nextAggregateValueDue)
);
// Shares due is the +/- diff or the total shares outstanding already mintedreturnint256(sharesDueForAggregateValueDue).sub(
int256(
FeeManager(FEE_MANAGER).getFeeSharesOutstandingForFund(
_comptrollerProxy,
address(this)
)
)
);
}
/// @dev Helper to calculate the max of two uint valuesfunction__calcUint256Max(uint256 _a, uint256 _b) privatepurereturns (uint256) {
if (_a >= _b) {
return _a;
}
return _b;
}
/// @dev Helper to settle the fee and update performance state./// Validated:/// _gav > 0function__settleAndUpdatePerformance(address _comptrollerProxy,
address _vaultProxy,
uint256 _gav
) privatereturns (int256 sharesDue_) {
ERC20 sharesTokenContract = ERC20(_vaultProxy);
uint256 totalSharesSupply = sharesTokenContract.totalSupply();
if (totalSharesSupply ==0) {
return0;
}
uint256 totalSharesOutstanding = sharesTokenContract.balanceOf(_vaultProxy);
if (totalSharesOutstanding == totalSharesSupply) {
return0;
}
FeeInfo storage feeInfo = comptrollerProxyToFeeInfo[_comptrollerProxy];
uint256 prevAggregateValueDue = feeInfo.aggregateValueDue;
uint256 nextAggregateValueDue;
(nextAggregateValueDue, sharesDue_) = __calcPerformance(
_comptrollerProxy,
totalSharesSupply,
totalSharesOutstanding,
prevAggregateValueDue,
feeInfo,
_gav
);
if (nextAggregateValueDue == prevAggregateValueDue) {
return0;
}
// Update fee state
feeInfo.aggregateValueDue = nextAggregateValueDue;
emit PerformanceUpdated(
_comptrollerProxy,
prevAggregateValueDue,
nextAggregateValueDue,
sharesDue_
);
return sharesDue_;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the feeInfo for a given fund/// @param _comptrollerProxy The ComptrollerProxy contract of the fund/// @return feeInfo_ The feeInfofunctiongetFeeInfoForFund(address _comptrollerProxy)
externalviewreturns (FeeInfo memory feeInfo_)
{
return comptrollerProxyToFeeInfo[_comptrollerProxy];
}
}
Contract Source Code
File 110 of 138: PermissionedVaultActionMixin.sol
// 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.
*/pragmasolidity 0.6.12;import"../../core/fund/comptroller/IComptroller.sol";
/// @title PermissionedVaultActionMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract for extensions that can make permissioned vault callsabstractcontractPermissionedVaultActionMixin{
/// @notice Adds a tracked asset to the fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _asset The asset to addfunction__addTrackedAsset(address _comptrollerProxy, address _asset) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.AddTrackedAsset,
abi.encode(_asset)
);
}
/// @notice Grants an allowance to a spender to use a fund's asset/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _asset The asset for which to grant an allowance/// @param _target The spender of the allowance/// @param _amount The amount of the allowancefunction__approveAssetSpender(address _comptrollerProxy,
address _asset,
address _target,
uint256 _amount
) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.ApproveAssetSpender,
abi.encode(_asset, _target, _amount)
);
}
/// @notice Burns fund shares for a particular account/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _target The account for which to burn shares/// @param _amount The amount of shares to burnfunction__burnShares(address _comptrollerProxy,
address _target,
uint256 _amount
) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.BurnShares,
abi.encode(_target, _amount)
);
}
/// @notice Mints fund shares to a particular account/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _target The account to which to mint shares/// @param _amount The amount of shares to mintfunction__mintShares(address _comptrollerProxy,
address _target,
uint256 _amount
) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.MintShares,
abi.encode(_target, _amount)
);
}
/// @notice Removes a tracked asset from the fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _asset The asset to removefunction__removeTrackedAsset(address _comptrollerProxy, address _asset) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.RemoveTrackedAsset,
abi.encode(_asset)
);
}
/// @notice Transfers fund shares from one account to another/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _from The account from which to transfer shares/// @param _to The account to which to transfer shares/// @param _amount The amount of shares to transferfunction__transferShares(address _comptrollerProxy,
address _from,
address _to,
uint256 _amount
) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.TransferShares,
abi.encode(_from, _to, _amount)
);
}
/// @notice Withdraws an asset from the VaultProxy to a given account/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _asset The asset to withdraw/// @param _target The account to which to withdraw the asset/// @param _amount The amount of asset to withdrawfunction__withdrawAssetTo(address _comptrollerProxy,
address _asset,
address _target,
uint256 _amount
) internal{
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.WithdrawAssetTo,
abi.encode(_asset, _target, _amount)
);
}
}
Contract Source Code
File 111 of 138: PolicyBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../IPolicy.sol";
/// @title PolicyBase Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Abstract base contract for all policiesabstractcontractPolicyBaseisIPolicy{
addressinternalimmutable POLICY_MANAGER;
modifieronlyPolicyManager{
require(msg.sender== POLICY_MANAGER, "Only the PolicyManager can make this call");
_;
}
constructor(address _policyManager) public{
POLICY_MANAGER = _policyManager;
}
/// @notice Validates and initializes a policy as necessary prior to fund activation/// @dev Unimplemented by default, can be overridden by the policyfunctionactivateForFund(address, address) externalvirtualoverride{
return;
}
/// @notice Updates the policy settings for a fund/// @dev Disallowed by default, can be overridden by the policyfunctionupdateFundSettings(address,
address,
bytescalldata) externalvirtualoverride{
revert("updateFundSettings: Updates not allowed for this policy");
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `POLICY_MANAGER` variable value/// @return policyManager_ The `POLICY_MANAGER` variable valuefunctiongetPolicyManager() externalviewreturns (address policyManager_) {
return POLICY_MANAGER;
}
}
Contract Source Code
File 112 of 138: PolicyManager.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/utils/EnumerableSet.sol";
import"../../core/fund/vault/IVault.sol";
import"../utils/ExtensionBase.sol";
import"../utils/FundDeployerOwnerMixin.sol";
import"./IPolicy.sol";
import"./IPolicyManager.sol";
/// @title PolicyManager Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Manages policies for fundscontractPolicyManagerisIPolicyManager, ExtensionBase, FundDeployerOwnerMixin{
usingEnumerableSetforEnumerableSet.AddressSet;
eventPolicyDeregistered(addressindexed policy, stringindexed identifier);
eventPolicyDisabledForFund(addressindexed comptrollerProxy, addressindexed policy);
eventPolicyEnabledForFund(addressindexed comptrollerProxy,
addressindexed policy,
bytes settingsData
);
eventPolicyRegistered(addressindexed policy,
stringindexed identifier,
PolicyHook[] implementedHooks
);
EnumerableSet.AddressSet private registeredPolicies;
mapping(address=>mapping(PolicyHook =>bool)) private policyToHookToIsImplemented;
mapping(address=> EnumerableSet.AddressSet) private comptrollerProxyToPolicies;
modifieronlyBuySharesHooks(address _policy) {
require(
!policyImplementsHook(_policy, PolicyHook.PreCallOnIntegration) &&!policyImplementsHook(_policy, PolicyHook.PostCallOnIntegration),
"onlyBuySharesHooks: Disallowed hook"
);
_;
}
modifieronlyEnabledPolicyForFund(address _comptrollerProxy, address _policy) {
require(
policyIsEnabledForFund(_comptrollerProxy, _policy),
"onlyEnabledPolicyForFund: Policy not enabled"
);
_;
}
constructor(address _fundDeployer) publicFundDeployerOwnerMixin(_fundDeployer) {}
// EXTERNAL FUNCTIONS/// @notice Validates and initializes policies as necessary prior to fund activation/// @param _isMigratedFund True if the fund is migrating to this release/// @dev Caller is expected to be a valid ComptrollerProxy, but there isn't a need to validate.functionactivateForFund(bool _isMigratedFund) externaloverride{
address vaultProxy = __setValidatedVaultProxy(msg.sender);
// Policies must assert that they are congruent with migrated vault stateif (_isMigratedFund) {
address[] memory enabledPolicies = getEnabledPoliciesForFund(msg.sender);
for (uint256 i; i < enabledPolicies.length; i++) {
__activatePolicyForFund(msg.sender, vaultProxy, enabledPolicies[i]);
}
}
}
/// @notice Deactivates policies for a fund by destroying storagefunctiondeactivateForFund() externaloverride{
delete comptrollerProxyToVaultProxy[msg.sender];
for (uint256 i = comptrollerProxyToPolicies[msg.sender].length(); i >0; i--) {
comptrollerProxyToPolicies[msg.sender].remove(
comptrollerProxyToPolicies[msg.sender].at(i -1)
);
}
}
/// @notice Disables a policy for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _policy The policy address to disablefunctiondisablePolicyForFund(address _comptrollerProxy, address _policy)
externalonlyBuySharesHooks(_policy)
onlyEnabledPolicyForFund(_comptrollerProxy, _policy)
{
__validateIsFundOwner(getVaultProxyForFund(_comptrollerProxy), msg.sender);
comptrollerProxyToPolicies[_comptrollerProxy].remove(_policy);
emit PolicyDisabledForFund(_comptrollerProxy, _policy);
}
/// @notice Enables a policy for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _policy The policy address to enable/// @param _settingsData The encoded settings data with which to configure the policy/// @dev Disabling a policy does not delete fund config on the policy, so if a policy is/// disabled and then enabled again, its initial state will be the previous config. It is the/// policy's job to determine how to merge that config with the _settingsData param in this function.functionenablePolicyForFund(address _comptrollerProxy,
address _policy,
bytescalldata _settingsData
) externalonlyBuySharesHooks(_policy) {
address vaultProxy = getVaultProxyForFund(_comptrollerProxy);
__validateIsFundOwner(vaultProxy, msg.sender);
__enablePolicyForFund(_comptrollerProxy, _policy, _settingsData);
__activatePolicyForFund(_comptrollerProxy, vaultProxy, _policy);
}
/// @notice Enable policies for use in a fund/// @param _configData Encoded config data/// @dev Only called during init() on ComptrollerProxy deploymentfunctionsetConfigForFund(bytescalldata _configData) externaloverride{
(address[] memory policies, bytes[] memory settingsData) =abi.decode(
_configData,
(address[], bytes[])
);
// Sanity checkrequire(
policies.length== settingsData.length,
"setConfigForFund: policies and settingsData array lengths unequal"
);
// Enable each policy with settingsfor (uint256 i; i < policies.length; i++) {
__enablePolicyForFund(msg.sender, policies[i], settingsData[i]);
}
}
/// @notice Updates policy settings for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _policy The Policy contract to update/// @param _settingsData The encoded settings data with which to update the policy configfunctionupdatePolicySettingsForFund(address _comptrollerProxy,
address _policy,
bytescalldata _settingsData
) externalonlyBuySharesHooks(_policy) onlyEnabledPolicyForFund(_comptrollerProxy, _policy) {
address vaultProxy = getVaultProxyForFund(_comptrollerProxy);
__validateIsFundOwner(vaultProxy, msg.sender);
IPolicy(_policy).updateFundSettings(_comptrollerProxy, vaultProxy, _settingsData);
}
/// @notice Validates all policies that apply to a given hook for a fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @param _hook The PolicyHook for which to validate policies/// @param _validationData The encoded data with which to validate the filtered policiesfunctionvalidatePolicies(address _comptrollerProxy,
PolicyHook _hook,
bytescalldata _validationData
) externaloverride{
address vaultProxy = getVaultProxyForFund(_comptrollerProxy);
address[] memory policies = getEnabledPoliciesForFund(_comptrollerProxy);
for (uint256 i; i < policies.length; i++) {
if (!policyImplementsHook(policies[i], _hook)) {
continue;
}
require(
IPolicy(policies[i]).validateRule(
_comptrollerProxy,
vaultProxy,
_hook,
_validationData
),
string(
abi.encodePacked(
"Rule evaluated to false: ",
IPolicy(policies[i]).identifier()
)
)
);
}
}
// PRIVATE FUNCTIONS/// @dev Helper to activate a policy for a fundfunction__activatePolicyForFund(address _comptrollerProxy,
address _vaultProxy,
address _policy
) private{
IPolicy(_policy).activateForFund(_comptrollerProxy, _vaultProxy);
}
/// @dev Helper to set config and enable policies for a fundfunction__enablePolicyForFund(address _comptrollerProxy,
address _policy,
bytesmemory _settingsData
) private{
require(
!policyIsEnabledForFund(_comptrollerProxy, _policy),
"__enablePolicyForFund: policy already enabled"
);
require(policyIsRegistered(_policy), "__enablePolicyForFund: Policy is not registered");
// Set fund config on policyif (_settingsData.length>0) {
IPolicy(_policy).addFundSettings(_comptrollerProxy, _settingsData);
}
// Add policy
comptrollerProxyToPolicies[_comptrollerProxy].add(_policy);
emit PolicyEnabledForFund(_comptrollerProxy, _policy, _settingsData);
}
/// @dev Helper to validate fund owner./// Preferred to a modifier because allows gas savings if re-using _vaultProxy.function__validateIsFundOwner(address _vaultProxy, address _who) privateview{
require(
_who == IVault(_vaultProxy).getOwner(),
"Only the fund owner can call this function"
);
}
///////////////////////// POLICIES REGISTRY //////////////////////////// @notice Remove policies from the list of registered policies/// @param _policies Addresses of policies to be registeredfunctionderegisterPolicies(address[] calldata _policies) externalonlyFundDeployerOwner{
require(_policies.length>0, "deregisterPolicies: _policies cannot be empty");
for (uint256 i; i < _policies.length; i++) {
require(
policyIsRegistered(_policies[i]),
"deregisterPolicies: policy is not registered"
);
registeredPolicies.remove(_policies[i]);
emit PolicyDeregistered(_policies[i], IPolicy(_policies[i]).identifier());
}
}
/// @notice Add policies to the list of registered policies/// @param _policies Addresses of policies to be registeredfunctionregisterPolicies(address[] calldata _policies) externalonlyFundDeployerOwner{
require(_policies.length>0, "registerPolicies: _policies cannot be empty");
for (uint256 i; i < _policies.length; i++) {
require(
!policyIsRegistered(_policies[i]),
"registerPolicies: policy already registered"
);
registeredPolicies.add(_policies[i]);
// Store the hooks that a policy implements for later use.// Fronts the gas for calls to check if a hook is implemented, and guarantees// that the implementsHooks return value does not change post-registration.
IPolicy policyContract = IPolicy(_policies[i]);
PolicyHook[] memory implementedHooks = policyContract.implementedHooks();
for (uint256 j; j < implementedHooks.length; j++) {
policyToHookToIsImplemented[_policies[i]][implementedHooks[j]] =true;
}
emit PolicyRegistered(_policies[i], policyContract.identifier(), implementedHooks);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Get all registered policies/// @return registeredPoliciesArray_ A list of all registered policy addressesfunctiongetRegisteredPolicies()
externalviewreturns (address[] memory registeredPoliciesArray_)
{
registeredPoliciesArray_ =newaddress[](registeredPolicies.length());
for (uint256 i; i < registeredPoliciesArray_.length; i++) {
registeredPoliciesArray_[i] = registeredPolicies.at(i);
}
}
/// @notice Get a list of enabled policies for a given fund/// @param _comptrollerProxy The ComptrollerProxy of the fund/// @return enabledPolicies_ An array of enabled policy addressesfunctiongetEnabledPoliciesForFund(address _comptrollerProxy)
publicviewreturns (address[] memory enabledPolicies_)
{
enabledPolicies_ =newaddress[](comptrollerProxyToPolicies[_comptrollerProxy].length());
for (uint256 i; i < enabledPolicies_.length; i++) {
enabledPolicies_[i] = comptrollerProxyToPolicies[_comptrollerProxy].at(i);
}
}
/// @notice Checks if a policy implements a particular hook/// @param _policy The address of the policy to check/// @param _hook The PolicyHook to check/// @return implementsHook_ True if the policy implements the hookfunctionpolicyImplementsHook(address _policy, PolicyHook _hook)
publicviewreturns (bool implementsHook_)
{
return policyToHookToIsImplemented[_policy][_hook];
}
/// @notice Check if a policy is enabled for the fund/// @param _comptrollerProxy The ComptrollerProxy of the fund to check/// @param _policy The address of the policy to check/// @return isEnabled_ True if the policy is enabled for the fundfunctionpolicyIsEnabledForFund(address _comptrollerProxy, address _policy)
publicviewreturns (bool isEnabled_)
{
return comptrollerProxyToPolicies[_comptrollerProxy].contains(_policy);
}
/// @notice Check whether a policy is registered/// @param _policy The address of the policy to check/// @return isRegistered_ True if the policy is registeredfunctionpolicyIsRegistered(address _policy) publicviewreturns (bool isRegistered_) {
return registeredPolicies.contains(_policy);
}
}
Contract Source Code
File 113 of 138: PostCallOnIntegrationValidatePolicyBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/PolicyBase.sol";
/// @title CallOnIntegrationPostValidatePolicyMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract for policies that only implement the PostCallOnIntegration policy hookabstractcontractPostCallOnIntegrationValidatePolicyBaseisPolicyBase{
/// @notice Gets the implemented PolicyHooks for a policy/// @return implementedHooks_ The implemented PolicyHooksfunctionimplementedHooks()
externalviewoverridereturns (IPolicyManager.PolicyHook[] memory implementedHooks_)
{
implementedHooks_ =new IPolicyManager.PolicyHook[](1);
implementedHooks_[0] = IPolicyManager.PolicyHook.PostCallOnIntegration;
return implementedHooks_;
}
/// @notice Helper to decode rule argumentsfunction__decodeRuleArgs(bytesmemory _encodedRuleArgs)
internalpurereturns (address adapter_,
bytes4 selector_,
address[] memory incomingAssets_,
uint256[] memory incomingAssetAmounts_,
address[] memory outgoingAssets_,
uint256[] memory outgoingAssetAmounts_
)
{
returnabi.decode(
_encodedRuleArgs,
(address, bytes4, address[], uint256[], address[], uint256[])
);
}
}
Contract Source Code
File 114 of 138: PreBuySharesValidatePolicyBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/PolicyBase.sol";
/// @title BuySharesPolicyMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract for policies that only implement the PreBuyShares policy hookabstractcontractPreBuySharesValidatePolicyBaseisPolicyBase{
/// @notice Gets the implemented PolicyHooks for a policy/// @return implementedHooks_ The implemented PolicyHooksfunctionimplementedHooks()
externalviewoverridereturns (IPolicyManager.PolicyHook[] memory implementedHooks_)
{
implementedHooks_ =new IPolicyManager.PolicyHook[](1);
implementedHooks_[0] = IPolicyManager.PolicyHook.PreBuyShares;
return implementedHooks_;
}
/// @notice Helper to decode rule argumentsfunction__decodeRuleArgs(bytesmemory _encodedArgs)
internalpurereturns (address buyer_,
uint256 investmentAmount_,
uint256 minSharesQuantity_,
uint256 gav_
)
{
returnabi.decode(_encodedArgs, (address, uint256, uint256, uint256));
}
}
Contract Source Code
File 115 of 138: PreCallOnIntegrationValidatePolicyBase.sol
// 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.
*/pragmasolidity 0.6.12;import"../../utils/PolicyBase.sol";
/// @title CallOnIntegrationPreValidatePolicyMixin Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A mixin contract for policies that only implement the PreCallOnIntegration policy hookabstractcontractPreCallOnIntegrationValidatePolicyBaseisPolicyBase{
/// @notice Gets the implemented PolicyHooks for a policy/// @return implementedHooks_ The implemented PolicyHooksfunctionimplementedHooks()
externalviewoverridereturns (IPolicyManager.PolicyHook[] memory implementedHooks_)
{
implementedHooks_ =new IPolicyManager.PolicyHook[](1);
implementedHooks_[0] = IPolicyManager.PolicyHook.PreCallOnIntegration;
return implementedHooks_;
}
/// @notice Helper to decode rule argumentsfunction__decodeRuleArgs(bytesmemory _encodedRuleArgs)
internalpurereturns (address adapter_, bytes4 selector_)
{
returnabi.decode(_encodedRuleArgs, (address, bytes4));
}
}
Contract Source Code
File 116 of 138: ProxiableVaultLib.sol
// 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.
*/pragmasolidity 0.6.12;/// @title ProxiableVaultLib Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A contract that defines the upgrade behavior for VaultLib instances/// @dev The recommended implementation of the target of a proxy according to EIP-1822 and EIP-1967/// Code position in storage is `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`,/// which is "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc".abstractcontractProxiableVaultLib{
/// @dev Updates the target of the proxy to be the contract at _nextVaultLibfunction__updateCodeAddress(address _nextVaultLib) internal{
require(
bytes32(0x027b9570e9fedc1a80b937ae9a06861e5faef3992491af30b684a64b3fbec7a5) ==
ProxiableVaultLib(_nextVaultLib).proxiableUUID(),
"__updateCodeAddress: _nextVaultLib not compatible"
);
assembly {
// solium-disable-linesstore(
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
_nextVaultLib
)
}
}
/// @notice Returns a unique bytes32 hash for VaultLib instances/// @return uuid_ The bytes32 hash representing the UUID/// @dev The UUID is `bytes32(keccak256('mln.proxiable.vaultlib'))`functionproxiableUUID() publicpurereturns (bytes32 uuid_) {
return0x027b9570e9fedc1a80b937ae9a06861e5faef3992491af30b684a64b3fbec7a5;
}
}
Contract Source Code
File 117 of 138: Proxy.sol
// 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.
*/pragmasolidity 0.6.12;/// @title Proxy Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A proxy contract for all Proxy instances/// @dev The recommended implementation of a Proxy in EIP-1822, updated for solc 0.6.12,/// and using the EIP-1967 storage slot for the proxiable implementation./// i.e., `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`, which is/// "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"/// See: https://eips.ethereum.org/EIPS/eip-1822contractProxy{
constructor(bytesmemory _constructData, address _contractLogic) public{
assembly {
sstore(
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
_contractLogic
)
}
(bool success, bytesmemory returnData) = _contractLogic.delegatecall(_constructData);
require(success, string(returnData));
}
fallback() externalpayable{
assembly {
let contractLogic :=sload(
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
)
calldatacopy(0x0, 0x0, calldatasize())
let success :=delegatecall(
sub(gas(), 10000),
contractLogic,
0x0,
calldatasize(),
0,
0
)
let retSz :=returndatasize()
returndatacopy(0, 0, retSz)
switch success
case0 {
revert(0, retSz)
}
default {
return(0, retSz)
}
}
}
}
Contract Source Code
File 118 of 138: RateProviderBase.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"./EthConstantMixin.sol";
abstractcontractRateProviderBaseisEthConstantMixin{
mapping(address=>mapping(address=>uint256)) public assetToAssetRate;
// Handles non-ERC20 compliant assets like ETH and USDmapping(address=>uint8) public specialAssetToDecimals;
constructor(address[] memory _specialAssets, uint8[] memory _specialAssetDecimals) public{
require(
_specialAssets.length== _specialAssetDecimals.length,
"constructor: _specialAssets and _specialAssetDecimals are uneven lengths"
);
for (uint256 i =0; i < _specialAssets.length; i++) {
specialAssetToDecimals[_specialAssets[i]] = _specialAssetDecimals[i];
}
specialAssetToDecimals[ETH_ADDRESS] =18;
}
function__getDecimalsForAsset(address _asset) internalviewreturns (uint256) {
uint256 decimals = specialAssetToDecimals[_asset];
if (decimals ==0) {
decimals =uint256(ERC20(_asset).decimals());
}
return decimals;
}
function__getRate(address _baseAsset, address _quoteAsset)
internalviewvirtualreturns (uint256)
{
return assetToAssetRate[_baseAsset][_quoteAsset];
}
functionsetRates(address[] calldata _baseAssets,
address[] calldata _quoteAssets,
uint256[] calldata _rates
) external{
require(
_baseAssets.length== _quoteAssets.length,
"setRates: _baseAssets and _quoteAssets are uneven lengths"
);
require(
_baseAssets.length== _rates.length,
"setRates: _baseAssets and _rates are uneven lengths"
);
for (uint256 i =0; i < _baseAssets.length; i++) {
assetToAssetRate[_baseAssets[i]][_quoteAssets[i]] = _rates[i];
}
}
}
Contract Source Code
File 119 of 138: SafeERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;import"./IERC20.sol";
import"../../math/SafeMath.sol";
import"../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingSafeMathforuint256;
usingAddressforaddress;
functionsafeTransfer(IERC20 token, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/functionsafeApprove(IERC20 token, address spender, uint256 value) internal{
// safeApprove should only be called when setting an initial allowance,// or when resetting it to zero. To increase and decrease it, use// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'// solhint-disable-next-line max-line-lengthrequire((value ==0) || (token.allowance(address(this), spender) ==0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
functionsafeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal{
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
functionsafeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal{
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) { // Return data is optional// solhint-disable-next-line max-line-lengthrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Contract Source Code
File 120 of 138: SafeMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.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.
*/librarySafeMath{
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (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.
*/functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(uint256 a, uint256 b) internalpurereturns (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/522if (a ==0) {
return0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts 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.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
*/functiondiv(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b >0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't holdreturn c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts 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.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message 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.
*/functionmod(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b !=0, errorMessage);
return a % b;
}
}
Contract Source Code
File 121 of 138: SharesTokenBase.sol
// 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.
*/pragmasolidity 0.6.12;import"./VaultLibSafeMath.sol";
/// @title StandardERC20 Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Contains the storage, events, and default logic of an ERC20-compliant contract./// @dev The logic can be overridden by VaultLib implementations./// Adapted from OpenZeppelin 3.2.0./// DO NOT EDIT THIS CONTRACT.abstractcontractSharesTokenBase{
usingVaultLibSafeMathforuint256;
eventApproval(addressindexed owner, addressindexed spender, uint256 value);
eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
stringinternal sharesName;
stringinternal sharesSymbol;
uint256internal sharesTotalSupply;
mapping(address=>uint256) internal sharesBalances;
mapping(address=>mapping(address=>uint256)) internal sharesAllowances;
// EXTERNAL FUNCTIONS/// @dev Standard implementation of ERC20's approve(). Can be overridden.functionapprove(address _spender, uint256 _amount) publicvirtualreturns (bool) {
__approve(msg.sender, _spender, _amount);
returntrue;
}
/// @dev Standard implementation of ERC20's transfer(). Can be overridden.functiontransfer(address _recipient, uint256 _amount) publicvirtualreturns (bool) {
__transfer(msg.sender, _recipient, _amount);
returntrue;
}
/// @dev Standard implementation of ERC20's transferFrom(). Can be overridden.functiontransferFrom(address _sender,
address _recipient,
uint256 _amount
) publicvirtualreturns (bool) {
__transfer(_sender, _recipient, _amount);
__approve(
_sender,
msg.sender,
sharesAllowances[_sender][msg.sender].sub(
_amount,
"ERC20: transfer amount exceeds allowance"
)
);
returntrue;
}
// EXTERNAL FUNCTIONS - VIEW/// @dev Standard implementation of ERC20's allowance(). Can be overridden.functionallowance(address _owner, address _spender) publicviewvirtualreturns (uint256) {
return sharesAllowances[_owner][_spender];
}
/// @dev Standard implementation of ERC20's balanceOf(). Can be overridden.functionbalanceOf(address _account) publicviewvirtualreturns (uint256) {
return sharesBalances[_account];
}
/// @dev Standard implementation of ERC20's decimals(). Can not be overridden.functiondecimals() publicpurereturns (uint8) {
return18;
}
/// @dev Standard implementation of ERC20's name(). Can be overridden.functionname() publicviewvirtualreturns (stringmemory) {
return sharesName;
}
/// @dev Standard implementation of ERC20's symbol(). Can be overridden.functionsymbol() publicviewvirtualreturns (stringmemory) {
return sharesSymbol;
}
/// @dev Standard implementation of ERC20's totalSupply(). Can be overridden.functiontotalSupply() publicviewvirtualreturns (uint256) {
return sharesTotalSupply;
}
// INTERNAL FUNCTIONS/// @dev Helper for approve(). Can be overridden.function__approve(address _owner,
address _spender,
uint256 _amount
) internalvirtual{
require(_owner !=address(0), "ERC20: approve from the zero address");
require(_spender !=address(0), "ERC20: approve to the zero address");
sharesAllowances[_owner][_spender] = _amount;
emit Approval(_owner, _spender, _amount);
}
/// @dev Helper to burn tokens from an account. Can be overridden.function__burn(address _account, uint256 _amount) internalvirtual{
require(_account !=address(0), "ERC20: burn from the zero address");
sharesBalances[_account] = sharesBalances[_account].sub(
_amount,
"ERC20: burn amount exceeds balance"
);
sharesTotalSupply = sharesTotalSupply.sub(_amount);
emit Transfer(_account, address(0), _amount);
}
/// @dev Helper to mint tokens to an account. Can be overridden.function__mint(address _account, uint256 _amount) internalvirtual{
require(_account !=address(0), "ERC20: mint to the zero address");
sharesTotalSupply = sharesTotalSupply.add(_amount);
sharesBalances[_account] = sharesBalances[_account].add(_amount);
emit Transfer(address(0), _account, _amount);
}
/// @dev Helper to transfer tokens between accounts. Can be overridden.function__transfer(address _sender,
address _recipient,
uint256 _amount
) internalvirtual{
require(_sender !=address(0), "ERC20: transfer from the zero address");
require(_recipient !=address(0), "ERC20: transfer to the zero address");
sharesBalances[_sender] = sharesBalances[_sender].sub(
_amount,
"ERC20: transfer amount exceeds balance"
);
sharesBalances[_recipient] = sharesBalances[_recipient].add(_amount);
emit Transfer(_sender, _recipient, _amount);
}
}
Contract Source Code
File 122 of 138: SignedSafeMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
*/librarySignedSafeMath{
int256constantprivate _INT256_MIN =-2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(int256 a, int256 b) internalpurereturns (int256) {
// 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/522if (a ==0) {
return0;
}
require(!(a ==-1&& b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two signed integers. Reverts 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.
*/functiondiv(int256 a, int256 b) internalpurereturns (int256) {
require(b !=0, "SignedSafeMath: division by zero");
require(!(b ==-1&& a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(int256 a, int256 b) internalpurereturns (int256) {
int256 c = a - b;
require((b >=0&& c <= a) || (b <0&& c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(int256 a, int256 b) internalpurereturns (int256) {
int256 c = a + b;
require((b >=0&& c >= a) || (b <0&& c < a), "SignedSafeMath: addition overflow");
return c;
}
}
Contract Source Code
File 123 of 138: SimpleMockIntegrateeBase.sol
// 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.
*/pragmasolidity 0.6.12;import"./MockIntegrateeBase.sol";
abstractcontractSimpleMockIntegrateeBaseisMockIntegrateeBase{
constructor(address[] memory _defaultRateAssets,
address[] memory _specialAssets,
uint8[] memory _specialAssetDecimals,
uint256 _ratePrecision
)
publicMockIntegrateeBase(
_defaultRateAssets,
_specialAssets,
_specialAssetDecimals,
_ratePrecision
)
{}
function__getRateAndSwapAssets(addresspayable _trader,
address _srcToken,
uint256 _srcAmount,
address _destToken
) internalreturns (uint256 destAmount_) {
uint256 actualRate = __getRate(_srcToken, _destToken);
__swapAssets(_trader, _srcToken, _srcAmount, _destToken, actualRate);
return actualRate;
}
}
Contract Source Code
File 124 of 138: SwapperBase.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"./EthConstantMixin.sol";
abstractcontractSwapperBaseisEthConstantMixin{
receive() externalpayable{}
function__swapAssets(addresspayable _trader,
address _srcToken,
uint256 _srcAmount,
address _destToken,
uint256 _actualRate
) internalreturns (uint256 destAmount_) {
address[] memory assetsToIntegratee =newaddress[](1);
assetsToIntegratee[0] = _srcToken;
uint256[] memory assetsToIntegrateeAmounts =newuint256[](1);
assetsToIntegrateeAmounts[0] = _srcAmount;
address[] memory assetsFromIntegratee =newaddress[](1);
assetsFromIntegratee[0] = _destToken;
uint256[] memory assetsFromIntegrateeAmounts =newuint256[](1);
assetsFromIntegrateeAmounts[0] = _actualRate;
__swap(
_trader,
assetsToIntegratee,
assetsToIntegrateeAmounts,
assetsFromIntegratee,
assetsFromIntegrateeAmounts
);
return assetsFromIntegrateeAmounts[0];
}
function__swap(addresspayable _trader,
address[] memory _assetsToIntegratee,
uint256[] memory _assetsToIntegrateeAmounts,
address[] memory _assetsFromIntegratee,
uint256[] memory _assetsFromIntegrateeAmounts
) internal{
// Take custody of incoming assetsfor (uint256 i =0; i < _assetsToIntegratee.length; i++) {
address asset = _assetsToIntegratee[i];
uint256 amount = _assetsToIntegrateeAmounts[i];
require(asset !=address(0), "__swap: empty value in _assetsToIntegratee");
require(amount >0, "__swap: empty value in _assetsToIntegrateeAmounts");
// Incoming ETH amounts can be ignoredif (asset == ETH_ADDRESS) {
continue;
}
ERC20(asset).transferFrom(_trader, address(this), amount);
}
// Distribute outgoing assetsfor (uint256 i =0; i < _assetsFromIntegratee.length; i++) {
address asset = _assetsFromIntegratee[i];
uint256 amount = _assetsFromIntegrateeAmounts[i];
require(asset !=address(0), "__swap: empty value in _assetsFromIntegratee");
require(amount >0, "__swap: empty value in _assetsFromIntegrateeAmounts");
if (asset == ETH_ADDRESS) {
_trader.transfer(amount);
} else {
ERC20(asset).transfer(_trader, amount);
}
}
}
}
Contract Source Code
File 125 of 138: SynthetixAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../infrastructure/price-feeds/derivatives/feeds/SynthetixPriceFeed.sol";
import"../../../../interfaces/ISynthetix.sol";
import"../utils/AdapterBase.sol";
/// @title SynthetixAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for interacting with SynthetixcontractSynthetixAdapterisAdapterBase{
addressprivateimmutable ORIGINATOR;
addressprivateimmutable SYNTHETIX;
addressprivateimmutable SYNTHETIX_PRICE_FEED;
bytes32privateimmutable TRACKING_CODE;
constructor(address _integrationManager,
address _synthetixPriceFeed,
address _originator,
address _synthetix,
bytes32 _trackingCode
) publicAdapterBase(_integrationManager) {
ORIGINATOR = _originator;
SYNTHETIX = _synthetix;
SYNTHETIX_PRICE_FEED = _synthetixPriceFeed;
TRACKING_CODE = _trackingCode;
}
// EXTERNAL FUNCTIONS/// @notice Provides a constant string identifier for an adapter/// @return identifier_ An identifier stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"SYNTHETIX";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
require(_selector == TAKE_ORDER_SELECTOR, "parseAssetsForMethod: _selector invalid");
(
address incomingAsset,
uint256 minIncomingAssetAmount,
address outgoingAsset,
uint256 outgoingAssetAmount
) = __decodeCallArgs(_encodedCallArgs);
spendAssets_ =newaddress[](1);
spendAssets_[0] = outgoingAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = incomingAsset;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minIncomingAssetAmount;
return (
IIntegrationManager.SpendAssetsHandleType.None,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Trades assets on Synthetix/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parametersfunctiontakeOrder(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata) externalonlyIntegrationManager{
(
address incomingAsset,
,
address outgoingAsset,
uint256 outgoingAssetAmount
) = __decodeCallArgs(_encodedCallArgs);
address[] memory synths =newaddress[](2);
synths[0] = outgoingAsset;
synths[1] = incomingAsset;
bytes32[] memory currencyKeys = SynthetixPriceFeed(SYNTHETIX_PRICE_FEED)
.getCurrencyKeysForSynths(synths);
ISynthetix(SYNTHETIX).exchangeOnBehalfWithTracking(
_vaultProxy,
currencyKeys[0],
outgoingAssetAmount,
currencyKeys[1],
ORIGINATOR,
TRACKING_CODE
);
}
// PRIVATE FUNCTIONS/// @dev Helper to decode the encoded call argumentsfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address incomingAsset_,
uint256 minIncomingAssetAmount_,
address outgoingAsset_,
uint256 outgoingAssetAmount_
)
{
returnabi.decode(_encodedCallArgs, (address, uint256, address, uint256));
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `ORIGINATOR` variable/// @return originator_ The `ORIGINATOR` variable valuefunctiongetOriginator() externalviewreturns (address originator_) {
return ORIGINATOR;
}
/// @notice Gets the `SYNTHETIX` variable/// @return synthetix_ The `SYNTHETIX` variable valuefunctiongetSynthetix() externalviewreturns (address synthetix_) {
return SYNTHETIX;
}
/// @notice Gets the `SYNTHETIX_PRICE_FEED` variable/// @return synthetixPriceFeed_ The `SYNTHETIX_PRICE_FEED` variable valuefunctiongetSynthetixPriceFeed() externalviewreturns (address synthetixPriceFeed_) {
return SYNTHETIX_PRICE_FEED;
}
/// @notice Gets the `TRACKING_CODE` variable/// @return trackingCode_ The `TRACKING_CODE` variable valuefunctiongetTrackingCode() externalviewreturns (bytes32 trackingCode_) {
return TRACKING_CODE;
}
}
Contract Source Code
File 126 of 138: SynthetixPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/ISynthetix.sol";
import"../../../../interfaces/ISynthetixAddressResolver.sol";
import"../../../../interfaces/ISynthetixExchangeRates.sol";
import"../../../../interfaces/ISynthetixProxyERC20.sol";
import"../../../../interfaces/ISynthetixSynth.sol";
import"../../../utils/DispatcherOwnerMixin.sol";
import"../IDerivativePriceFeed.sol";
/// @title SynthetixPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A price feed that uses Synthetix oracles as price sourcescontractSynthetixPriceFeedisIDerivativePriceFeed, DispatcherOwnerMixin{
usingSafeMathforuint256;
eventSynthAdded(addressindexed synth, bytes32 currencyKey);
eventSynthCurrencyKeyUpdated(addressindexed synth,
bytes32 prevCurrencyKey,
bytes32 nextCurrencyKey
);
uint256privateconstant SYNTH_UNIT =10**18;
addressprivateimmutable ADDRESS_RESOLVER;
addressprivateimmutable SUSD;
mapping(address=>bytes32) private synthToCurrencyKey;
constructor(address _dispatcher,
address _addressResolver,
address _sUSD,
address[] memory _synths
) publicDispatcherOwnerMixin(_dispatcher) {
ADDRESS_RESOLVER = _addressResolver;
SUSD = _sUSD;
address[] memory sUSDSynths =newaddress[](1);
sUSDSynths[0] = _sUSD;
__addSynths(sUSDSynths);
__addSynths(_synths);
}
/// @notice Converts a given amount of a derivative to its underlying asset values/// @param _derivative The derivative to convert/// @param _derivativeAmount The amount of the derivative to convert/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amountfunctioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
underlyings_ =newaddress[](1);
underlyings_[0] = SUSD;
underlyingAmounts_ =newuint256[](1);
bytes32 currencyKey = getCurrencyKeyForSynth(_derivative);
require(currencyKey !=0, "calcUnderlyingValues: _derivative is not supported");
address exchangeRates = ISynthetixAddressResolver(ADDRESS_RESOLVER).requireAndGetAddress(
"ExchangeRates",
"calcUnderlyingValues: Missing ExchangeRates"
);
(uint256 rate, bool isInvalid) = ISynthetixExchangeRates(exchangeRates).rateAndInvalid(
currencyKey
);
require(!isInvalid, "calcUnderlyingValues: _derivative rate is not valid");
underlyingAmounts_[0] = _derivativeAmount.mul(rate).div(SYNTH_UNIT);
return (underlyings_, underlyingAmounts_);
}
/// @notice Checks whether an asset is a supported primitive of the price feed/// @param _asset The asset to check/// @return isSupported_ True if the asset is a supported primitivefunctionisSupportedAsset(address _asset) publicviewoverridereturns (bool isSupported_) {
return getCurrencyKeyForSynth(_asset) !=0;
}
/////////////////////// SYNTHS REGISTRY ////////////////////////// @notice Adds Synths to the price feed/// @param _synths Synths to addfunctionaddSynths(address[] calldata _synths) externalonlyDispatcherOwner{
require(_synths.length>0, "addSynths: Empty _synths");
__addSynths(_synths);
}
/// @notice Updates the cached currencyKey value for specified Synths/// @param _synths Synths to update/// @dev Anybody can call this functionfunctionupdateSynthCurrencyKeys(address[] calldata _synths) external{
require(_synths.length>0, "updateSynthCurrencyKeys: Empty _synths");
for (uint256 i; i < _synths.length; i++) {
bytes32 prevCurrencyKey = synthToCurrencyKey[_synths[i]];
require(prevCurrencyKey !=0, "updateSynthCurrencyKeys: Synth not set");
bytes32 nextCurrencyKey = __getCurrencyKey(_synths[i]);
require(
nextCurrencyKey != prevCurrencyKey,
"updateSynthCurrencyKeys: Synth has correct currencyKey"
);
synthToCurrencyKey[_synths[i]] = nextCurrencyKey;
emit SynthCurrencyKeyUpdated(_synths[i], prevCurrencyKey, nextCurrencyKey);
}
}
/// @dev Helper to add Synthsfunction__addSynths(address[] memory _synths) private{
for (uint256 i; i < _synths.length; i++) {
require(synthToCurrencyKey[_synths[i]] ==0, "__addSynths: Value already set");
bytes32 currencyKey = __getCurrencyKey(_synths[i]);
require(currencyKey !=0, "__addSynths: No currencyKey");
synthToCurrencyKey[_synths[i]] = currencyKey;
emit SynthAdded(_synths[i], currencyKey);
}
}
/// @dev Helper to query a currencyKey from Synthetixfunction__getCurrencyKey(address _synthProxy) privateviewreturns (bytes32 currencyKey_) {
return ISynthetixSynth(ISynthetixProxyERC20(_synthProxy).target()).currencyKey();
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `ADDRESS_RESOLVER` variable/// @return addressResolver_ The `ADDRESS_RESOLVER` variable valuefunctiongetAddressResolver() externalviewreturns (address) {
return ADDRESS_RESOLVER;
}
/// @notice Gets the currencyKey for multiple given Synths/// @return currencyKeys_ The currencyKey valuesfunctiongetCurrencyKeysForSynths(address[] calldata _synths)
externalviewreturns (bytes32[] memory currencyKeys_)
{
currencyKeys_ =newbytes32[](_synths.length);
for (uint256 i; i < _synths.length; i++) {
currencyKeys_[i] = synthToCurrencyKey[_synths[i]];
}
return currencyKeys_;
}
/// @notice Gets the `SUSD` variable/// @return susd_ The `SUSD` variable valuefunctiongetSUSD() externalviewreturns (address susd_) {
return SUSD;
}
/// @notice Gets the currencyKey for a given Synth/// @return currencyKey_ The currencyKey valuefunctiongetCurrencyKeyForSynth(address _synth) publicviewreturns (bytes32 currencyKey_) {
return synthToCurrencyKey[_synth];
}
}
Contract Source Code
File 127 of 138: TrackedAssetsAdapter.sol
// 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.
*/pragmasolidity 0.6.12;import"../../../../core/fund/vault/VaultLib.sol";
import"../utils/AdapterBase.sol";
/// @title TrackedAssetsAdapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter to add tracked assets to a fund (useful e.g. to handle token airdrops)contractTrackedAssetsAdapterisAdapterBase{
constructor(address _integrationManager) publicAdapterBase(_integrationManager) {}
/// @notice Add multiple assets to the Vault's list of tracked assets/// @dev No need to perform any validation or implement any logicfunctionaddTrackedAssets(address,
bytescalldata,
bytescalldata) externalview{}
/// @notice Provides a constant string identifier for an adapter/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"TRACKED_ASSETS";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
require(
_selector == ADD_TRACKED_ASSETS_SELECTOR,
"parseAssetsForMethod: _selector invalid"
);
incomingAssets_ = __decodeCallArgs(_encodedCallArgs);
minIncomingAssetAmounts_ =newuint256[](incomingAssets_.length);
for (uint256 i; i < minIncomingAssetAmounts_.length; i++) {
minIncomingAssetAmounts_[i] =1;
}
return (
spendAssetsHandleType_,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
// PRIVATE FUNCTIONS/// @dev Helper to decode the encoded call argumentsfunction__decodeCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address[] memory incomingAssets_)
{
returnabi.decode(_encodedCallArgs, (address[]));
}
}
Contract Source Code
File 128 of 138: UniswapV2Adapter.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/IUniswapV2Factory.sol";
import"../../../../interfaces/IUniswapV2Router2.sol";
import"../utils/AdapterBase.sol";
/// @title UniswapV2Adapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter for interacting with Uniswap v2contractUniswapV2AdapterisAdapterBase{
usingSafeMathforuint256;
addressprivateimmutable FACTORY;
addressprivateimmutable ROUTER;
constructor(address _integrationManager,
address _router,
address _factory
) publicAdapterBase(_integrationManager) {
FACTORY = _factory;
ROUTER = _router;
}
// EXTERNAL FUNCTIONS/// @notice Provides a constant string identifier for an adapter/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"UNISWAP_V2";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
if (_selector == LEND_SELECTOR) {
(
address[2] memory outgoingAssets,
uint256[2] memory maxOutgoingAssetAmounts,
,
uint256 minIncomingAssetAmount
) = __decodeLendCallArgs(_encodedCallArgs);
spendAssets_ =newaddress[](2);
spendAssets_[0] = outgoingAssets[0];
spendAssets_[1] = outgoingAssets[1];
spendAssetAmounts_ =newuint256[](2);
spendAssetAmounts_[0] = maxOutgoingAssetAmounts[0];
spendAssetAmounts_[1] = maxOutgoingAssetAmounts[1];
incomingAssets_ =newaddress[](1);
// No need to validate not address(0), this will be caught in IntegrationManager
incomingAssets_[0] = IUniswapV2Factory(FACTORY).getPair(
outgoingAssets[0],
outgoingAssets[1]
);
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minIncomingAssetAmount;
} elseif (_selector == REDEEM_SELECTOR) {
(
uint256 outgoingAssetAmount,
address[2] memory incomingAssets,
uint256[2] memory minIncomingAssetAmounts
) = __decodeRedeemCallArgs(_encodedCallArgs);
spendAssets_ =newaddress[](1);
// No need to validate not address(0), this will be caught in IntegrationManager
spendAssets_[0] = IUniswapV2Factory(FACTORY).getPair(
incomingAssets[0],
incomingAssets[1]
);
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount;
incomingAssets_ =newaddress[](2);
incomingAssets_[0] = incomingAssets[0];
incomingAssets_[1] = incomingAssets[1];
minIncomingAssetAmounts_ =newuint256[](2);
minIncomingAssetAmounts_[0] = minIncomingAssetAmounts[0];
minIncomingAssetAmounts_[1] = minIncomingAssetAmounts[1];
} elseif (_selector == TAKE_ORDER_SELECTOR) {
(
address[] memory path,
uint256 outgoingAssetAmount,
uint256 minIncomingAssetAmount
) = __decodeTakeOrderCallArgs(_encodedCallArgs);
require(path.length>=2, "parseAssetsForMethod: _path must be >= 2");
spendAssets_ =newaddress[](1);
spendAssets_[0] = path[0];
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = outgoingAssetAmount;
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = path[path.length-1];
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = minIncomingAssetAmount;
} else {
revert("parseAssetsForMethod: _selector invalid");
}
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Lends assets for pool tokens on Uniswap/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionlend(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(
address[2] memory outgoingAssets,
uint256[2] memory maxOutgoingAssetAmounts,
uint256[2] memory minOutgoingAssetAmounts,
) = __decodeLendCallArgs(_encodedCallArgs);
__lend(
_vaultProxy,
outgoingAssets[0],
outgoingAssets[1],
maxOutgoingAssetAmounts[0],
maxOutgoingAssetAmounts[1],
minOutgoingAssetAmounts[0],
minOutgoingAssetAmounts[1]
);
}
/// @notice Redeems pool tokens on Uniswap/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctionredeem(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(
uint256 outgoingAssetAmount,
address[2] memory incomingAssets,
uint256[2] memory minIncomingAssetAmounts
) = __decodeRedeemCallArgs(_encodedCallArgs);
// More efficient to parse pool token from _encodedAssetTransferArgs than external call
(, address[] memory spendAssets, , ) = __decodeEncodedAssetTransferArgs(
_encodedAssetTransferArgs
);
__redeem(
_vaultProxy,
spendAssets[0],
outgoingAssetAmount,
incomingAssets[0],
incomingAssets[1],
minIncomingAssetAmounts[0],
minIncomingAssetAmounts[1]
);
}
/// @notice Trades assets on Uniswap/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctiontakeOrder(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(
address[] memory path,
uint256 outgoingAssetAmount,
uint256 minIncomingAssetAmount
) = __decodeTakeOrderCallArgs(_encodedCallArgs);
__takeOrder(_vaultProxy, outgoingAssetAmount, minIncomingAssetAmount, path);
}
// PRIVATE FUNCTIONS/// @dev Helper to decode the lend encoded call argumentsfunction__decodeLendCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address[2] memory outgoingAssets_,
uint256[2] memory maxOutgoingAssetAmounts_,
uint256[2] memory minOutgoingAssetAmounts_,
uint256 minIncomingAssetAmount_
)
{
returnabi.decode(_encodedCallArgs, (address[2], uint256[2], uint256[2], uint256));
}
/// @dev Helper to decode the redeem encoded call argumentsfunction__decodeRedeemCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (uint256 outgoingAssetAmount_,
address[2] memory incomingAssets_,
uint256[2] memory minIncomingAssetAmounts_
)
{
returnabi.decode(_encodedCallArgs, (uint256, address[2], uint256[2]));
}
/// @dev Helper to decode the take order encoded call argumentsfunction__decodeTakeOrderCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (address[] memory path_,
uint256 outgoingAssetAmount_,
uint256 minIncomingAssetAmount_
)
{
returnabi.decode(_encodedCallArgs, (address[], uint256, uint256));
}
/// @dev Helper to execute lend. Avoids stack-too-deep error.function__lend(address _vaultProxy,
address _tokenA,
address _tokenB,
uint256 _amountADesired,
uint256 _amountBDesired,
uint256 _amountAMin,
uint256 _amountBMin
) private{
__approveMaxAsNeeded(_tokenA, ROUTER, _amountADesired);
__approveMaxAsNeeded(_tokenB, ROUTER, _amountBDesired);
// Execute lend on Uniswap
IUniswapV2Router2(ROUTER).addLiquidity(
_tokenA,
_tokenB,
_amountADesired,
_amountBDesired,
_amountAMin,
_amountBMin,
_vaultProxy,
block.timestamp.add(1)
);
}
/// @dev Helper to execute redeem. Avoids stack-too-deep error.function__redeem(address _vaultProxy,
address _poolToken,
uint256 _poolTokenAmount,
address _tokenA,
address _tokenB,
uint256 _amountAMin,
uint256 _amountBMin
) private{
__approveMaxAsNeeded(_poolToken, ROUTER, _poolTokenAmount);
// Execute redeem on Uniswap
IUniswapV2Router2(ROUTER).removeLiquidity(
_tokenA,
_tokenB,
_poolTokenAmount,
_amountAMin,
_amountBMin,
_vaultProxy,
block.timestamp.add(1)
);
}
/// @dev Helper to execute takeOrder. Avoids stack-too-deep error.function__takeOrder(address _vaultProxy,
uint256 _outgoingAssetAmount,
uint256 _minIncomingAssetAmount,
address[] memory _path
) private{
__approveMaxAsNeeded(_path[0], ROUTER, _outgoingAssetAmount);
// Execute fill
IUniswapV2Router2(ROUTER).swapExactTokensForTokens(
_outgoingAssetAmount,
_minIncomingAssetAmount,
_path,
_vaultProxy,
block.timestamp.add(1)
);
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `FACTORY` variable/// @return factory_ The `FACTORY` variable valuefunctiongetFactory() externalviewreturns (address factory_) {
return FACTORY;
}
/// @notice Gets the `ROUTER` variable/// @return router_ The `ROUTER` variable valuefunctiongetRouter() externalviewreturns (address router_) {
return ROUTER;
}
}
Contract Source Code
File 129 of 138: UniswapV2PoolPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../../../../interfaces/IUniswapV2Pair.sol";
import"../../../../utils/MathHelpers.sol";
import"../../../utils/DispatcherOwnerMixin.sol";
import"../../../value-interpreter/ValueInterpreter.sol";
import"../../primitives/IPrimitivePriceFeed.sol";
import"../../utils/UniswapV2PoolTokenValueCalculator.sol";
import"../IDerivativePriceFeed.sol";
/// @title UniswapV2PoolPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Price feed for Uniswap lending pool tokenscontractUniswapV2PoolPriceFeedisIDerivativePriceFeed,
DispatcherOwnerMixin,
MathHelpers,
UniswapV2PoolTokenValueCalculator{
eventPoolTokenAdded(addressindexed poolToken, address token0, address token1);
structPoolTokenInfo {
address token0;
address token1;
uint8 token0Decimals;
uint8 token1Decimals;
}
uint256privateconstant POOL_TOKEN_UNIT =10**18;
addressprivateimmutable DERIVATIVE_PRICE_FEED;
addressprivateimmutable FACTORY;
addressprivateimmutable PRIMITIVE_PRICE_FEED;
addressprivateimmutable VALUE_INTERPRETER;
mapping(address=> PoolTokenInfo) private poolTokenToInfo;
constructor(address _dispatcher,
address _derivativePriceFeed,
address _primitivePriceFeed,
address _valueInterpreter,
address _factory,
address[] memory _poolTokens
) publicDispatcherOwnerMixin(_dispatcher) {
DERIVATIVE_PRICE_FEED = _derivativePriceFeed;
FACTORY = _factory;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
VALUE_INTERPRETER = _valueInterpreter;
__addPoolTokens(_poolTokens, _derivativePriceFeed, _primitivePriceFeed);
}
/// @notice Converts a given amount of a derivative to its underlying asset values/// @param _derivative The derivative to convert/// @param _derivativeAmount The amount of the derivative to convert/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amountfunctioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
PoolTokenInfo memory poolTokenInfo = poolTokenToInfo[_derivative];
underlyings_ =newaddress[](2);
underlyings_[0] = poolTokenInfo.token0;
underlyings_[1] = poolTokenInfo.token1;
// Calculate the amounts underlying one unit of a pool token,// taking into account the known, trusted rate between the two underlyings
(uint256 token0TrustedRateAmount, uint256 token1TrustedRateAmount) = __calcTrustedRate(
poolTokenInfo.token0,
poolTokenInfo.token1,
poolTokenInfo.token0Decimals,
poolTokenInfo.token1Decimals
);
(
uint256 token0DenormalizedRate,
uint256 token1DenormalizedRate
) = __calcTrustedPoolTokenValue(
FACTORY,
_derivative,
token0TrustedRateAmount,
token1TrustedRateAmount
);
// Define normalized rates for each underlying
underlyingAmounts_ =newuint256[](2);
underlyingAmounts_[0] = _derivativeAmount.mul(token0DenormalizedRate).div(POOL_TOKEN_UNIT);
underlyingAmounts_[1] = _derivativeAmount.mul(token1DenormalizedRate).div(POOL_TOKEN_UNIT);
return (underlyings_, underlyingAmounts_);
}
/// @notice Checks if an asset is supported by the price feed/// @param _asset The asset to check/// @return isSupported_ True if the asset is supportedfunctionisSupportedAsset(address _asset) publicviewoverridereturns (bool isSupported_) {
return poolTokenToInfo[_asset].token0 !=address(0);
}
// PRIVATE FUNCTIONS/// @dev Calculates the trusted rate of two assets based on our price feeds./// Uses the decimals-derived unit for whichever asset is used as the quote asset.function__calcTrustedRate(address _token0,
address _token1,
uint256 _token0Decimals,
uint256 _token1Decimals
) privatereturns (uint256 token0RateAmount_, uint256 token1RateAmount_) {
bool rateIsValid;
// The quote asset of the value lookup must be a supported primitive asset,// so we cycle through the tokens until reaching a primitive.// If neither is a primitive, will revert at the ValueInterpreterif (IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_token0)) {
token1RateAmount_ =10**_token1Decimals;
(token0RateAmount_, rateIsValid) = ValueInterpreter(VALUE_INTERPRETER)
.calcCanonicalAssetValue(_token1, token1RateAmount_, _token0);
} else {
token0RateAmount_ =10**_token0Decimals;
(token1RateAmount_, rateIsValid) = ValueInterpreter(VALUE_INTERPRETER)
.calcCanonicalAssetValue(_token0, token0RateAmount_, _token1);
}
require(rateIsValid, "__calcTrustedRate: Invalid rate");
return (token0RateAmount_, token1RateAmount_);
}
//////////////////////////// POOL TOKENS REGISTRY /////////////////////////////// @notice Adds Uniswap pool tokens to the price feed/// @param _poolTokens Uniswap pool tokens to addfunctionaddPoolTokens(address[] calldata _poolTokens) externalonlyDispatcherOwner{
require(_poolTokens.length>0, "addPoolTokens: Empty _poolTokens");
__addPoolTokens(_poolTokens, DERIVATIVE_PRICE_FEED, PRIMITIVE_PRICE_FEED);
}
/// @dev Helper to add Uniswap pool tokensfunction__addPoolTokens(address[] memory _poolTokens,
address _derivativePriceFeed,
address _primitivePriceFeed
) private{
for (uint256 i; i < _poolTokens.length; i++) {
require(_poolTokens[i] !=address(0), "__addPoolTokens: Empty poolToken");
require(
poolTokenToInfo[_poolTokens[i]].token0 ==address(0),
"__addPoolTokens: Value already set"
);
IUniswapV2Pair uniswapV2Pair = IUniswapV2Pair(_poolTokens[i]);
address token0 = uniswapV2Pair.token0();
address token1 = uniswapV2Pair.token1();
require(
__poolTokenIsSupportable(
_derivativePriceFeed,
_primitivePriceFeed,
token0,
token1
),
"__addPoolTokens: Unsupported pool token"
);
poolTokenToInfo[_poolTokens[i]] = PoolTokenInfo({
token0: token0,
token1: token1,
token0Decimals: ERC20(token0).decimals(),
token1Decimals: ERC20(token1).decimals()
});
emit PoolTokenAdded(_poolTokens[i], token0, token1);
}
}
/// @dev Helper to determine if a pool token is supportable, based on whether price feeds are/// available for its underlying feeds. At least one of the underlying tokens must be/// a supported primitive asset, and the other must be a primitive or derivative.function__poolTokenIsSupportable(address _derivativePriceFeed,
address _primitivePriceFeed,
address _token0,
address _token1
) privateviewreturns (bool isSupportable_) {
IDerivativePriceFeed derivativePriceFeedContract = IDerivativePriceFeed(
_derivativePriceFeed
);
IPrimitivePriceFeed primitivePriceFeedContract = IPrimitivePriceFeed(_primitivePriceFeed);
if (primitivePriceFeedContract.isSupportedAsset(_token0)) {
if (
primitivePriceFeedContract.isSupportedAsset(_token1) ||
derivativePriceFeedContract.isSupportedAsset(_token1)
) {
returntrue;
}
} elseif (
derivativePriceFeedContract.isSupportedAsset(_token0) &&
primitivePriceFeedContract.isSupportedAsset(_token1)
) {
returntrue;
}
returnfalse;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `DERIVATIVE_PRICE_FEED` variable value/// @return derivativePriceFeed_ The `DERIVATIVE_PRICE_FEED` variable valuefunctiongetDerivativePriceFeed() externalviewreturns (address derivativePriceFeed_) {
return DERIVATIVE_PRICE_FEED;
}
/// @notice Gets the `FACTORY` variable value/// @return factory_ The `FACTORY` variable valuefunctiongetFactory() externalviewreturns (address factory_) {
return FACTORY;
}
/// @notice Gets the `PoolTokenInfo` for a given pool token/// @param _poolToken The pool token for which to get the `PoolTokenInfo`/// @return poolTokenInfo_ The `PoolTokenInfo` valuefunctiongetPoolTokenInfo(address _poolToken)
externalviewreturns (PoolTokenInfo memory poolTokenInfo_)
{
return poolTokenToInfo[_poolToken];
}
/// @notice Gets the underlyings for a given pool token/// @param _poolToken The pool token for which to get its underlyings/// @return token0_ The UniswapV2Pair.token0 value/// @return token1_ The UniswapV2Pair.token1 valuefunctiongetPoolTokenUnderlyings(address _poolToken)
externalviewreturns (address token0_, address token1_)
{
return (poolTokenToInfo[_poolToken].token0, poolTokenToInfo[_poolToken].token1);
}
/// @notice Gets the `PRIMITIVE_PRICE_FEED` variable value/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable valuefunctiongetPrimitivePriceFeed() externalviewreturns (address primitivePriceFeed_) {
return PRIMITIVE_PRICE_FEED;
}
/// @notice Gets the `VALUE_INTERPRETER` variable value/// @return valueInterpreter_ The `VALUE_INTERPRETER` variable valuefunctiongetValueInterpreter() externalviewreturns (address valueInterpreter_) {
return VALUE_INTERPRETER;
}
}
Contract Source Code
File 130 of 138: UniswapV2PoolTokenValueCalculator.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../interfaces/IUniswapV2Factory.sol";
import"../../../interfaces/IUniswapV2Pair.sol";
/// @title UniswapV2PoolTokenValueCalculator Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Abstract contract for computing the value of Uniswap liquidity pool tokens/// @dev Unless otherwise noted, these functions are adapted to our needs and style guide from/// an un-merged Uniswap branch:/// https://github.com/Uniswap/uniswap-v2-periphery/blob/267ba44471f3357071a2fe2573fe4da42d5ad969/contracts/libraries/UniswapV2LiquidityMathLibrary.solabstractcontractUniswapV2PoolTokenValueCalculator{
usingSafeMathforuint256;
uint256privateconstant POOL_TOKEN_UNIT =10**18;
// INTERNAL FUNCTIONS/// @dev Given a Uniswap pool with token0 and token1 and their trusted rate,/// returns the value of one pool token unit in terms of token0 and token1./// This is the only function used outside of this contract.function__calcTrustedPoolTokenValue(address _factory,
address _pair,
uint256 _token0TrustedRateAmount,
uint256 _token1TrustedRateAmount
) internalviewreturns (uint256 token0Amount_, uint256 token1Amount_) {
(uint256 reserve0, uint256 reserve1) = __calcReservesAfterArbitrage(
_pair,
_token0TrustedRateAmount,
_token1TrustedRateAmount
);
return __calcPoolTokenValue(_factory, _pair, reserve0, reserve1);
}
// PRIVATE FUNCTIONS/// @dev Computes liquidity value given all the parameters of the pairfunction__calcPoolTokenValue(address _factory,
address _pair,
uint256 _reserve0,
uint256 _reserve1
) privateviewreturns (uint256 token0Amount_, uint256 token1Amount_) {
IUniswapV2Pair pairContract = IUniswapV2Pair(_pair);
uint256 totalSupply = pairContract.totalSupply();
if (IUniswapV2Factory(_factory).feeTo() !=address(0)) {
uint256 kLast = pairContract.kLast();
if (kLast >0) {
uint256 rootK = __uniswapSqrt(_reserve0.mul(_reserve1));
uint256 rootKLast = __uniswapSqrt(kLast);
if (rootK > rootKLast) {
uint256 numerator = totalSupply.mul(rootK.sub(rootKLast));
uint256 denominator = rootK.mul(5).add(rootKLast);
uint256 feeLiquidity = numerator.div(denominator);
totalSupply = totalSupply.add(feeLiquidity);
}
}
}
return (
_reserve0.mul(POOL_TOKEN_UNIT).div(totalSupply),
_reserve1.mul(POOL_TOKEN_UNIT).div(totalSupply)
);
}
/// @dev Calculates the direction and magnitude of the profit-maximizing tradefunction__calcProfitMaximizingTrade(uint256 _token0TrustedRateAmount,
uint256 _token1TrustedRateAmount,
uint256 _reserve0,
uint256 _reserve1
) privatepurereturns (bool token0ToToken1_, uint256 amountIn_) {
token0ToToken1_ =
_reserve0.mul(_token1TrustedRateAmount).div(_reserve1) < _token0TrustedRateAmount;
uint256 leftSide;
uint256 rightSide;
if (token0ToToken1_) {
leftSide = __uniswapSqrt(
_reserve0.mul(_reserve1).mul(_token0TrustedRateAmount).mul(1000).div(
_token1TrustedRateAmount.mul(997)
)
);
rightSide = _reserve0.mul(1000).div(997);
} else {
leftSide = __uniswapSqrt(
_reserve0.mul(_reserve1).mul(_token1TrustedRateAmount).mul(1000).div(
_token0TrustedRateAmount.mul(997)
)
);
rightSide = _reserve1.mul(1000).div(997);
}
if (leftSide < rightSide) {
return (false, 0);
}
// Calculate the amount that must be sent to move the price to the profit-maximizing price
amountIn_ = leftSide.sub(rightSide);
return (token0ToToken1_, amountIn_);
}
/// @dev Calculates the pool reserves after an arbitrage moves the price to/// the profit-maximizing rate, given an externally-observed trusted rate/// between the two pooled assetsfunction__calcReservesAfterArbitrage(address _pair,
uint256 _token0TrustedRateAmount,
uint256 _token1TrustedRateAmount
) privateviewreturns (uint256 reserve0_, uint256 reserve1_) {
(reserve0_, reserve1_, ) = IUniswapV2Pair(_pair).getReserves();
// Skip checking whether the reserve is 0, as this is extremely unlikely given how// initial pool liquidity is locked, and since we maintain a list of registered pool tokens// Calculate how much to swap to arb to the trusted price
(bool token0ToToken1, uint256 amountIn) = __calcProfitMaximizingTrade(
_token0TrustedRateAmount,
_token1TrustedRateAmount,
reserve0_,
reserve1_
);
if (amountIn ==0) {
return (reserve0_, reserve1_);
}
// Adjust the reserves to account for the arb trade to the trusted priceif (token0ToToken1) {
uint256 amountOut = __uniswapV2GetAmountOut(amountIn, reserve0_, reserve1_);
reserve0_ = reserve0_.add(amountIn);
reserve1_ = reserve1_.sub(amountOut);
} else {
uint256 amountOut = __uniswapV2GetAmountOut(amountIn, reserve1_, reserve0_);
reserve1_ = reserve1_.add(amountIn);
reserve0_ = reserve0_.sub(amountOut);
}
return (reserve0_, reserve1_);
}
/// @dev Uniswap square root function. See:/// https://github.com/Uniswap/uniswap-lib/blob/6ddfedd5716ba85b905bf34d7f1f3c659101a1bc/contracts/libraries/Babylonian.solfunction__uniswapSqrt(uint256 _y) privatepurereturns (uint256 z_) {
if (_y >3) {
z_ = _y;
uint256 x = _y /2+1;
while (x < z_) {
z_ = x;
x = (_y / x + x) /2;
}
} elseif (_y !=0) {
z_ =1;
}
// else z_ = 0return z_;
}
/// @dev Simplified version of UniswapV2Library's getAmountOut() function. See:/// https://github.com/Uniswap/uniswap-v2-periphery/blob/87edfdcaf49ccc52591502993db4c8c08ea9eec0/contracts/libraries/UniswapV2Library.sol#L42-L50function__uniswapV2GetAmountOut(uint256 _amountIn,
uint256 _reserveIn,
uint256 _reserveOut
) privatepurereturns (uint256 amountOut_) {
uint256 amountInWithFee = _amountIn.mul(997);
uint256 numerator = amountInWithFee.mul(_reserveOut);
uint256 denominator = _reserveIn.mul(1000).add(amountInWithFee);
return numerator.div(denominator);
}
}
Contract Source Code
File 131 of 138: ValueInterpreter.sol
// 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.
*/pragmasolidity 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.contractValueInterpreterisIValueInterpreter{
usingSafeMathforuint256;
addressprivateimmutable AGGREGATED_DERIVATIVE_PRICE_FEED;
addressprivateimmutable PRIMITIVE_PRICE_FEED;
constructor(address _primitivePriceFeed, address _aggregatedDerivativePriceFeed) public{
AGGREGATED_DERIVATIVE_PRICE_FEED = _aggregatedDerivativePriceFeed;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
}
// EXTERNAL FUNCTIONS/// @notice An alias of calcCanonicalAssetsTotalValuefunctioncalcLiveAssetsTotalValue(address[] calldata _baseAssets,
uint256[] calldata _amounts,
address _quoteAsset
) externaloverridereturns (uint256 value_, bool isValid_) {
return calcCanonicalAssetsTotalValue(_baseAssets, _amounts, _quoteAsset);
}
/// @notice An alias of calcCanonicalAssetValuefunctioncalcLiveAssetValue(address _baseAsset,
uint256 _amount,
address _quoteAsset
) externaloverridereturns (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 statefunctioncalcCanonicalAssetsTotalValue(address[] memory _baseAssets,
uint256[] memory _amounts,
address _quoteAsset
) publicoverridereturns (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 statefunctioncalcCanonicalAssetValue(address _baseAsset,
uint256 _amount,
address _quoteAsset
) publicoverridereturns (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
) privatereturns (uint256 value_, bool isValid_) {
if (_baseAsset == _quoteAsset || _amount ==0) {
return (_amount, true);
}
// Handle case that asset is a primitiveif (IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).isSupportedAsset(_baseAsset)) {
return
IPrimitivePriceFeed(PRIMITIVE_PRICE_FEED).calcCanonicalValue(
_baseAsset,
_amount,
_quoteAsset
);
}
// Handle case that asset is a derivativeaddress 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
) privatereturns (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 valuefunctiongetAggregatedDerivativePriceFeed()
externalviewreturns (address aggregatedDerivativePriceFeed_)
{
return AGGREGATED_DERIVATIVE_PRICE_FEED;
}
/// @notice Gets the `PRIMITIVE_PRICE_FEED` variable/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable valuefunctiongetPrimitivePriceFeed() externalviewreturns (address primitivePriceFeed_) {
return PRIMITIVE_PRICE_FEED;
}
}
Contract Source Code
File 132 of 138: VaultLib.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"../../../../persistent/dispatcher/IDispatcher.sol";
import"../../../../persistent/vault/VaultLibBase1.sol";
import"./IVault.sol";
/// @title VaultLib Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice The per-release proxiable library contract for VaultProxy/// @dev The difference in terminology between "asset" and "trackedAsset" is intentional./// A fund might actually have asset balances of un-tracked assets,/// but only tracked assets are used in gav calculations./// Note that this contract inherits VaultLibSafeMath (a verbatim Open Zeppelin SafeMath copy)/// from SharesTokenBase via VaultLibBase1contractVaultLibisVaultLibBase1, IVault{
usingSafeERC20forERC20;
// Before updating TRACKED_ASSETS_LIMIT in the future, it is important to consider:// 1. The highest tracked assets limit ever allowed in the protocol// 2. That the next value will need to be respected by all future releasesuint256privateconstant TRACKED_ASSETS_LIMIT =20;
modifieronlyAccessor() {
require(msg.sender== accessor, "Only the designated accessor can make this call");
_;
}
/////////////// GENERAL ////////////////// @notice Sets the account that is allowed to migrate a fund to new releases/// @param _nextMigrator The account to set as the allowed migrator/// @dev Set to address(0) to remove the migrator.functionsetMigrator(address _nextMigrator) external{
require(msg.sender== owner, "setMigrator: Only the owner can call this function");
address prevMigrator = migrator;
require(_nextMigrator != prevMigrator, "setMigrator: Value already set");
migrator = _nextMigrator;
emit MigratorSet(prevMigrator, _nextMigrator);
}
///////////// VAULT //////////////// @notice Adds a tracked asset to the fund/// @param _asset The asset to add/// @dev Allows addition of already tracked assets to fail silently.functionaddTrackedAsset(address _asset) externaloverrideonlyAccessor{
if (!isTrackedAsset(_asset)) {
require(
trackedAssets.length< TRACKED_ASSETS_LIMIT,
"addTrackedAsset: Limit exceeded"
);
assetToIsTracked[_asset] =true;
trackedAssets.push(_asset);
emit TrackedAssetAdded(_asset);
}
}
/// @notice Grants an allowance to a spender to use the fund's asset/// @param _asset The asset for which to grant an allowance/// @param _target The spender of the allowance/// @param _amount The amount of the allowancefunctionapproveAssetSpender(address _asset,
address _target,
uint256 _amount
) externaloverrideonlyAccessor{
ERC20(_asset).approve(_target, _amount);
}
/// @notice Makes an arbitrary call with this contract as the sender/// @param _contract The contract to call/// @param _callData The call data for the callfunctioncallOnContract(address _contract, bytescalldata _callData)
externaloverrideonlyAccessor{
(bool success, bytesmemory returnData) = _contract.call(_callData);
require(success, string(returnData));
}
/// @notice Removes a tracked asset from the fund/// @param _asset The asset to removefunctionremoveTrackedAsset(address _asset) externaloverrideonlyAccessor{
__removeTrackedAsset(_asset);
}
/// @notice Withdraws an asset from the VaultProxy to a given account/// @param _asset The asset to withdraw/// @param _target The account to which to withdraw the asset/// @param _amount The amount of asset to withdrawfunctionwithdrawAssetTo(address _asset,
address _target,
uint256 _amount
) externaloverrideonlyAccessor{
ERC20(_asset).safeTransfer(_target, _amount);
emit AssetWithdrawn(_asset, _target, _amount);
}
/// @dev Helper to the get the Vault's balance of a given assetfunction__getAssetBalance(address _asset) privateviewreturns (uint256 balance_) {
return ERC20(_asset).balanceOf(address(this));
}
/// @dev Helper to remove an asset from a fund's tracked assets./// Allows removal of non-tracked asset to fail silently.function__removeTrackedAsset(address _asset) private{
if (isTrackedAsset(_asset)) {
assetToIsTracked[_asset] =false;
uint256 trackedAssetsCount = trackedAssets.length;
for (uint256 i =0; i < trackedAssetsCount; i++) {
if (trackedAssets[i] == _asset) {
if (i < trackedAssetsCount -1) {
trackedAssets[i] = trackedAssets[trackedAssetsCount -1];
}
trackedAssets.pop();
break;
}
}
emit TrackedAssetRemoved(_asset);
}
}
////////////// SHARES ///////////////// @notice Burns fund shares from a particular account/// @param _target The account for which to burn shares/// @param _amount The amount of shares to burnfunctionburnShares(address _target, uint256 _amount) externaloverrideonlyAccessor{
__burn(_target, _amount);
}
/// @notice Mints fund shares to a particular account/// @param _target The account for which to burn shares/// @param _amount The amount of shares to mintfunctionmintShares(address _target, uint256 _amount) externaloverrideonlyAccessor{
__mint(_target, _amount);
}
/// @notice Transfers fund shares from one account to another/// @param _from The account from which to transfer shares/// @param _to The account to which to transfer shares/// @param _amount The amount of shares to transferfunctiontransferShares(address _from,
address _to,
uint256 _amount
) externaloverrideonlyAccessor{
__transfer(_from, _to, _amount);
}
// ERC20 overrides/// @dev Disallows the standard ERC20 approve() functionfunctionapprove(address, uint256) publicoverridereturns (bool) {
revert("Unimplemented");
}
/// @notice Gets the `symbol` value of the shares token/// @return symbol_ The `symbol` value/// @dev Defers the shares symbol value to the Dispatcher contractfunctionsymbol() publicviewoverridereturns (stringmemory symbol_) {
return IDispatcher(creator).getSharesTokenSymbol();
}
/// @dev Disallows the standard ERC20 transfer() functionfunctiontransfer(address, uint256) publicoverridereturns (bool) {
revert("Unimplemented");
}
/// @dev Disallows the standard ERC20 transferFrom() functionfunctiontransferFrom(address,
address,
uint256) publicoverridereturns (bool) {
revert("Unimplemented");
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `accessor` variable/// @return accessor_ The `accessor` variable valuefunctiongetAccessor() externalviewoverridereturns (address accessor_) {
return accessor;
}
/// @notice Gets the `creator` variable/// @return creator_ The `creator` variable valuefunctiongetCreator() externalviewreturns (address creator_) {
return creator;
}
/// @notice Gets the `migrator` variable/// @return migrator_ The `migrator` variable valuefunctiongetMigrator() externalviewreturns (address migrator_) {
return migrator;
}
/// @notice Gets the `owner` variable/// @return owner_ The `owner` variable valuefunctiongetOwner() externalviewoverridereturns (address owner_) {
return owner;
}
/// @notice Gets the `trackedAssets` variable/// @return trackedAssets_ The `trackedAssets` variable valuefunctiongetTrackedAssets() externalviewoverridereturns (address[] memory trackedAssets_) {
return trackedAssets;
}
/// @notice Check whether an address is a tracked asset of the fund/// @param _asset The address to check/// @return isTrackedAsset_ True if the address is a tracked asset of the fundfunctionisTrackedAsset(address _asset) publicviewoverridereturns (bool isTrackedAsset_) {
return assetToIsTracked[_asset];
}
}
Contract Source Code
File 133 of 138: VaultLibBase1.sol
// 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.
*/pragmasolidity 0.6.12;import"./VaultLibBaseCore.sol";
/// @title VaultLibBase1 Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice The first implementation of VaultLibBaseCore, with additional events and storage/// @dev All subsequent implementations should inherit the previous implementation,/// e.g., `VaultLibBase2 is VaultLibBase1`/// DO NOT EDIT CONTRACT.abstractcontractVaultLibBase1isVaultLibBaseCore{
eventAssetWithdrawn(addressindexed asset, addressindexed target, uint256 amount);
eventTrackedAssetAdded(address asset);
eventTrackedAssetRemoved(address asset);
address[] internal trackedAssets;
mapping(address=>bool) internal assetToIsTracked;
}
Contract Source Code
File 134 of 138: VaultLibBaseCore.sol
// 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.
*/pragmasolidity 0.6.12;import"../utils/IMigratableVault.sol";
import"./utils/ProxiableVaultLib.sol";
import"./utils/SharesTokenBase.sol";
/// @title VaultLibBaseCore Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A persistent contract containing all required storage variables and/// required functions for a VaultLib implementation/// @dev DO NOT EDIT CONTRACT. If new events or storage are necessary, they should be added to/// a numbered VaultLibBaseXXX that inherits the previous base. See VaultLibBase1.abstractcontractVaultLibBaseCoreisIMigratableVault, ProxiableVaultLib, SharesTokenBase{
eventAccessorSet(address prevAccessor, address nextAccessor);
eventMigratorSet(address prevMigrator, address nextMigrator);
eventOwnerSet(address prevOwner, address nextOwner);
eventVaultLibSet(address prevVaultLib, address nextVaultLib);
addressinternal accessor;
addressinternal creator;
addressinternal migrator;
addressinternal owner;
// EXTERNAL FUNCTIONS/// @notice Initializes the VaultProxy with core configuration/// @param _owner The address to set as the fund owner/// @param _accessor The address to set as the permissioned accessor of the VaultLib/// @param _fundName The name of the fund/// @dev Serves as a per-proxy pseudo-constructorfunctioninit(address _owner,
address _accessor,
stringcalldata _fundName
) externaloverride{
require(creator ==address(0), "init: Proxy already initialized");
creator =msg.sender;
sharesName = _fundName;
__setAccessor(_accessor);
__setOwner(_owner);
emit VaultLibSet(address(0), getVaultLib());
}
/// @notice Sets the permissioned accessor of the VaultLib/// @param _nextAccessor The address to set as the permissioned accessor of the VaultLibfunctionsetAccessor(address _nextAccessor) externaloverride{
require(msg.sender== creator, "setAccessor: Only callable by the contract creator");
__setAccessor(_nextAccessor);
}
/// @notice Sets the VaultLib target for the VaultProxy/// @param _nextVaultLib The address to set as the VaultLib/// @dev This function is absolutely critical. __updateCodeAddress() validates that the/// target is a valid Proxiable contract instance./// Does not block _nextVaultLib from being the same as the current VaultLibfunctionsetVaultLib(address _nextVaultLib) externaloverride{
require(msg.sender== creator, "setVaultLib: Only callable by the contract creator");
address prevVaultLib = getVaultLib();
__updateCodeAddress(_nextVaultLib);
emit VaultLibSet(prevVaultLib, _nextVaultLib);
}
// PUBLIC FUNCTIONS/// @notice Checks whether an account is allowed to migrate the VaultProxy/// @param _who The account to check/// @return canMigrate_ True if the account is allowed to migrate the VaultProxyfunctioncanMigrate(address _who) publicviewvirtualoverridereturns (bool canMigrate_) {
return _who == owner || _who == migrator;
}
/// @notice Gets the VaultLib target for the VaultProxy/// @return vaultLib_ The address of the VaultLib targetfunctiongetVaultLib() publicviewreturns (address vaultLib_) {
assembly {
// solium-disable-line
vaultLib_ :=sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
}
return vaultLib_;
}
// INTERNAL FUNCTIONS/// @dev Helper to set the permissioned accessor of the VaultProxy./// Does not prevent the prevAccessor from being the _nextAccessor.function__setAccessor(address _nextAccessor) internal{
require(_nextAccessor !=address(0), "__setAccessor: _nextAccessor cannot be empty");
address prevAccessor = accessor;
accessor = _nextAccessor;
emit AccessorSet(prevAccessor, _nextAccessor);
}
/// @dev Helper to set the owner of the VaultProxyfunction__setOwner(address _nextOwner) internal{
require(_nextOwner !=address(0), "__setOwner: _nextOwner cannot be empty");
address prevOwner = owner;
require(_nextOwner != prevOwner, "__setOwner: _nextOwner is the current owner");
owner = _nextOwner;
emit OwnerSet(prevOwner, _nextOwner);
}
}
Contract Source Code
File 135 of 138: VaultLibSafeMath.sol
// 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.
*/pragmasolidity 0.6.12;/// @title VaultLibSafeMath library/// @notice A narrowed, verbatim implementation of OpenZeppelin 3.2.0 SafeMath/// for use with VaultLib/// @dev Preferred to importing from npm to guarantee consistent logic and revert reasons/// between VaultLib implementations/// DO NOT EDIT THIS CONTRACTlibraryVaultLibSafeMath{
functionadd(uint256 a, uint256 b) internalpurereturns (uint256) {
uint256 c = a + b;
require(c >= a, "VaultLibSafeMath: addition overflow");
return c;
}
functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
return sub(a, b, "VaultLibSafeMath: subtraction overflow");
}
functionsub(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
functionmul(uint256 a, uint256 b) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
uint256 c = a * b;
require(c / a == b, "VaultLibSafeMath: multiplication overflow");
return c;
}
functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
return div(a, b, "VaultLibSafeMath: division by zero");
}
functiondiv(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
require(b >0, errorMessage);
uint256 c = a / b;
return c;
}
functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
return mod(a, b, "VaultLibSafeMath: modulo by zero");
}
functionmod(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
require(b !=0, errorMessage);
return a % b;
}
}
Contract Source Code
File 136 of 138: VaultProxy.sol
// 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.
*/pragmasolidity 0.6.12;import"./utils/ProxiableVaultLib.sol";
/// @title VaultProxy Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice A proxy contract for all VaultProxy instances, slightly modified from EIP-1822/// @dev Adapted from the recommended implementation of a Proxy in EIP-1822, updated for solc 0.6.12,/// and using the EIP-1967 storage slot for the proxiable implementation./// i.e., `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`, which is/// "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"/// See: https://eips.ethereum.org/EIPS/eip-1822contractVaultProxy{
constructor(bytesmemory _constructData, address _vaultLib) public{
// "0x027b9570e9fedc1a80b937ae9a06861e5faef3992491af30b684a64b3fbec7a5" corresponds to// `bytes32(keccak256('mln.proxiable.vaultlib'))`require(
bytes32(0x027b9570e9fedc1a80b937ae9a06861e5faef3992491af30b684a64b3fbec7a5) ==
ProxiableVaultLib(_vaultLib).proxiableUUID(),
"constructor: _vaultLib not compatible"
);
assembly {
// solium-disable-linesstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, _vaultLib)
}
(bool success, bytesmemory returnData) = _vaultLib.delegatecall(_constructData); // solium-disable-linerequire(success, string(returnData));
}
fallback() externalpayable{
assembly {
// solium-disable-linelet contractLogic :=sload(
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
)
calldatacopy(0x0, 0x0, calldatasize())
let success :=delegatecall(
sub(gas(), 10000),
contractLogic,
0x0,
calldatasize(),
0,
0
)
let retSz :=returndatasize()
returndatacopy(0, 0, retSz)
switch success
case0 {
revert(0, retSz)
}
default {
return(0, retSz)
}
}
}
}
Contract Source Code
File 137 of 138: WdgldPriceFeed.sol
// 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.
*/pragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/IChainlinkAggregator.sol";
import"../../../../utils/MakerDaoMath.sol";
import"../IDerivativePriceFeed.sol";
/// @title WdgldPriceFeed Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Price source oracle for WDGLD <https://dgld.ch/>contractWdgldPriceFeedisIDerivativePriceFeed, MakerDaoMath{
usingSafeMathforuint256;
addressprivateimmutable XAU_AGGREGATOR;
addressprivateimmutable ETH_AGGREGATOR;
addressprivateimmutable WDGLD;
addressprivateimmutable WETH;
// GTR_CONSTANT aggregates all the invariants in the GTR formula to save gasuint256privateconstant GTR_CONSTANT =999990821653213975346065101;
uint256privateconstant GTR_PRECISION =10**27;
uint256privateconstant WDGLD_GENESIS_TIMESTAMP =1568700000;
constructor(address _wdgld,
address _weth,
address _ethAggregator,
address _xauAggregator
) public{
WDGLD = _wdgld;
WETH = _weth;
ETH_AGGREGATOR = _ethAggregator;
XAU_AGGREGATOR = _xauAggregator;
}
/// @notice Converts a given amount of a derivative to its underlying asset values/// @param _derivative The derivative to convert/// @param _derivativeAmount The amount of the derivative to convert/// @return underlyings_ The underlying assets for the _derivative/// @return underlyingAmounts_ The amount of each underlying asset for the equivalent derivative amountfunctioncalcUnderlyingValues(address _derivative, uint256 _derivativeAmount)
externaloverridereturns (address[] memory underlyings_, uint256[] memory underlyingAmounts_)
{
require(isSupportedAsset(_derivative), "calcUnderlyingValues: Only WDGLD is supported");
underlyings_ =newaddress[](1);
underlyings_[0] = WETH;
underlyingAmounts_ =newuint256[](1);
// Get price rates from xau and eth aggregatorsint256 xauToUsdRate = IChainlinkAggregator(XAU_AGGREGATOR).latestAnswer();
int256 ethToUsdRate = IChainlinkAggregator(ETH_AGGREGATOR).latestAnswer();
require(xauToUsdRate >0&& ethToUsdRate >0, "calcUnderlyingValues: rate invalid");
uint256 wdgldToXauRate = calcWdgldToXauRate();
// 10**17 is a combination of ETH_UNIT / WDGLD_UNIT * GTR_PRECISION
underlyingAmounts_[0] = _derivativeAmount
.mul(wdgldToXauRate)
.mul(uint256(xauToUsdRate))
.div(uint256(ethToUsdRate))
.div(10**17);
return (underlyings_, underlyingAmounts_);
}
/// @notice Calculates the rate of WDGLD to XAU./// @return wdgldToXauRate_ The current rate of WDGLD to XAU/// @dev Full formula available <https://dgld.ch/assets/documents/dgld-whitepaper.pdf>functioncalcWdgldToXauRate() publicviewreturns (uint256 wdgldToXauRate_) {
return
__rpow(
GTR_CONSTANT,
((block.timestamp).sub(WDGLD_GENESIS_TIMESTAMP)).div(28800), // 60 * 60 * 8 (8 hour periods)
GTR_PRECISION
)
.div(10);
}
/// @notice Checks if an asset is supported by this price feed/// @param _asset The asset to check/// @return isSupported_ True if supportedfunctionisSupportedAsset(address _asset) publicviewoverridereturns (bool isSupported_) {
return _asset == WDGLD;
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `ETH_AGGREGATOR` address/// @return ethAggregatorAddress_ The `ETH_AGGREGATOR` addressfunctiongetEthAggregator() externalviewreturns (address ethAggregatorAddress_) {
return ETH_AGGREGATOR;
}
/// @notice Gets the `WDGLD` token address/// @return wdgld_ The `WDGLD` token addressfunctiongetWdgld() externalviewreturns (address wdgld_) {
return WDGLD;
}
/// @notice Gets the `WETH` token address/// @return weth_ The `WETH` token addressfunctiongetWeth() externalviewreturns (address weth_) {
return WETH;
}
/// @notice Gets the `XAU_AGGREGATOR` address/// @return xauAggregatorAddress_ The `XAU_AGGREGATOR` addressfunctiongetXauAggregator() externalviewreturns (address xauAggregatorAddress_) {
return XAU_AGGREGATOR;
}
}
Contract Source Code
File 138 of 138: ZeroExV2Adapter.sol
// 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.
*/pragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"../../../../interfaces/IZeroExV2.sol";
import"../../../../utils/MathHelpers.sol";
import"../../../../utils/AddressArrayLib.sol";
import"../../../utils/FundDeployerOwnerMixin.sol";
import"../utils/AdapterBase.sol";
/// @title ZeroExV2Adapter Contract/// @author Enzyme Council <security@enzyme.finance>/// @notice Adapter to 0xV2 Exchange ContractcontractZeroExV2AdapterisAdapterBase, FundDeployerOwnerMixin, MathHelpers{
usingAddressArrayLibforaddress[];
usingSafeMathforuint256;
eventAllowedMakerAdded(addressindexed account);
eventAllowedMakerRemoved(addressindexed account);
addressprivateimmutable EXCHANGE;
mapping(address=>bool) private makerToIsAllowed;
// Gas could be optimized for the end-user by also storing an immutable ZRX_ASSET_DATA,// for example, but in the narrow OTC use-case of this adapter, taker fees are unlikely.constructor(address _integrationManager,
address _exchange,
address _fundDeployer,
address[] memory _allowedMakers
) publicAdapterBase(_integrationManager) FundDeployerOwnerMixin(_fundDeployer) {
EXCHANGE = _exchange;
if (_allowedMakers.length>0) {
__addAllowedMakers(_allowedMakers);
}
}
// EXTERNAL FUNCTIONS/// @notice Provides a constant string identifier for an adapter/// @return identifier_ The identifer stringfunctionidentifier() externalpureoverridereturns (stringmemory identifier_) {
return"ZERO_EX_V2";
}
/// @notice Parses the expected assets to receive from a call on integration/// @param _selector The function selector for the callOnIntegration/// @param _encodedCallArgs The encoded parameters for the callOnIntegration/// @return spendAssetsHandleType_ A type that dictates how to handle granting/// the adapter access to spend assets (`None` by default)/// @return spendAssets_ The assets to spend in the call/// @return spendAssetAmounts_ The max asset amounts to spend in the call/// @return incomingAssets_ The assets to receive in the call/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the callfunctionparseAssetsForMethod(bytes4 _selector, bytescalldata _encodedCallArgs)
externalviewoverridereturns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
)
{
require(_selector == TAKE_ORDER_SELECTOR, "parseAssetsForMethod: _selector invalid");
(
bytesmemory encodedZeroExOrderArgs,
uint256 takerAssetFillAmount
) = __decodeTakeOrderCallArgs(_encodedCallArgs);
IZeroExV2.Order memory order = __constructOrderStruct(encodedZeroExOrderArgs);
require(
isAllowedMaker(order.makerAddress),
"parseAssetsForMethod: Order maker is not allowed"
);
require(
takerAssetFillAmount <= order.takerAssetAmount,
"parseAssetsForMethod: Taker asset fill amount greater than available"
);
address makerAsset = __getAssetAddress(order.makerAssetData);
address takerAsset = __getAssetAddress(order.takerAssetData);
// Format incoming assets
incomingAssets_ =newaddress[](1);
incomingAssets_[0] = makerAsset;
minIncomingAssetAmounts_ =newuint256[](1);
minIncomingAssetAmounts_[0] = __calcRelativeQuantity(
order.takerAssetAmount,
order.makerAssetAmount,
takerAssetFillAmount
);
if (order.takerFee >0) {
address takerFeeAsset = __getAssetAddress(IZeroExV2(EXCHANGE).ZRX_ASSET_DATA());
uint256 takerFeeFillAmount = __calcRelativeQuantity(
order.takerAssetAmount,
order.takerFee,
takerAssetFillAmount
); // fee calculated relative to taker fill amountif (takerFeeAsset == makerAsset) {
require(
order.takerFee < order.makerAssetAmount,
"parseAssetsForMethod: Fee greater than makerAssetAmount"
);
spendAssets_ =newaddress[](1);
spendAssets_[0] = takerAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = takerAssetFillAmount;
minIncomingAssetAmounts_[0] = minIncomingAssetAmounts_[0].sub(takerFeeFillAmount);
} elseif (takerFeeAsset == takerAsset) {
spendAssets_ =newaddress[](1);
spendAssets_[0] = takerAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = takerAssetFillAmount.add(takerFeeFillAmount);
} else {
spendAssets_ =newaddress[](2);
spendAssets_[0] = takerAsset;
spendAssets_[1] = takerFeeAsset;
spendAssetAmounts_ =newuint256[](2);
spendAssetAmounts_[0] = takerAssetFillAmount;
spendAssetAmounts_[1] = takerFeeFillAmount;
}
} else {
spendAssets_ =newaddress[](1);
spendAssets_[0] = takerAsset;
spendAssetAmounts_ =newuint256[](1);
spendAssetAmounts_[0] = takerAssetFillAmount;
}
return (
IIntegrationManager.SpendAssetsHandleType.Transfer,
spendAssets_,
spendAssetAmounts_,
incomingAssets_,
minIncomingAssetAmounts_
);
}
/// @notice Take an order on 0x/// @param _vaultProxy The VaultProxy of the calling fund/// @param _encodedCallArgs Encoded order parameters/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receivefunctiontakeOrder(address _vaultProxy,
bytescalldata _encodedCallArgs,
bytescalldata _encodedAssetTransferArgs
)
externalonlyIntegrationManagerfundAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
{
(
bytesmemory encodedZeroExOrderArgs,
uint256 takerAssetFillAmount
) = __decodeTakeOrderCallArgs(_encodedCallArgs);
IZeroExV2.Order memory order = __constructOrderStruct(encodedZeroExOrderArgs);
// Approve spend assets as needed
__approveMaxAsNeeded(
__getAssetAddress(order.takerAssetData),
__getAssetProxy(order.takerAssetData),
takerAssetFillAmount
);
// Ignores whether makerAsset or takerAsset overlap with the takerFee asset for simplicityif (order.takerFee >0) {
bytesmemory zrxData = IZeroExV2(EXCHANGE).ZRX_ASSET_DATA();
__approveMaxAsNeeded(
__getAssetAddress(zrxData),
__getAssetProxy(zrxData),
__calcRelativeQuantity(
order.takerAssetAmount,
order.takerFee,
takerAssetFillAmount
) // fee calculated relative to taker fill amount
);
}
// Execute order
(, , , bytesmemory signature) = __decodeZeroExOrderArgs(encodedZeroExOrderArgs);
IZeroExV2(EXCHANGE).fillOrder(order, takerAssetFillAmount, signature);
}
// PRIVATE FUNCTIONS/// @dev Parses user inputs into a ZeroExV2.Order formatfunction__constructOrderStruct(bytesmemory _encodedOrderArgs)
privatepurereturns (IZeroExV2.Order memory order_)
{
(
address[4] memory orderAddresses,
uint256[6] memory orderValues,
bytes[2] memory orderData,
) = __decodeZeroExOrderArgs(_encodedOrderArgs);
return
IZeroExV2.Order({
makerAddress: orderAddresses[0],
takerAddress: orderAddresses[1],
feeRecipientAddress: orderAddresses[2],
senderAddress: orderAddresses[3],
makerAssetAmount: orderValues[0],
takerAssetAmount: orderValues[1],
makerFee: orderValues[2],
takerFee: orderValues[3],
expirationTimeSeconds: orderValues[4],
salt: orderValues[5],
makerAssetData: orderData[0],
takerAssetData: orderData[1]
});
}
/// @dev Decode the parameters of a takeOrder call/// @param _encodedCallArgs Encoded parameters passed from client side/// @return encodedZeroExOrderArgs_ Encoded args of the 0x order/// @return takerAssetFillAmount_ Amount of taker asset to fillfunction__decodeTakeOrderCallArgs(bytesmemory _encodedCallArgs)
privatepurereturns (bytesmemory encodedZeroExOrderArgs_, uint256 takerAssetFillAmount_)
{
returnabi.decode(_encodedCallArgs, (bytes, uint256));
}
/// @dev Decode the parameters of a 0x order/// @param _encodedZeroExOrderArgs Encoded parameters of the 0x order/// @return orderAddresses_ Addresses used in the order/// - [0] 0x Order param: makerAddress/// - [1] 0x Order param: takerAddress/// - [2] 0x Order param: feeRecipientAddress/// - [3] 0x Order param: senderAddress/// @return orderValues_ Values used in the order/// - [0] 0x Order param: makerAssetAmount/// - [1] 0x Order param: takerAssetAmount/// - [2] 0x Order param: makerFee/// - [3] 0x Order param: takerFee/// - [4] 0x Order param: expirationTimeSeconds/// - [5] 0x Order param: salt/// @return orderData_ Bytes data used in the order/// - [0] 0x Order param: makerAssetData/// - [1] 0x Order param: takerAssetData/// @return signature_ Signature of the orderfunction__decodeZeroExOrderArgs(bytesmemory _encodedZeroExOrderArgs)
privatepurereturns (address[4] memory orderAddresses_,
uint256[6] memory orderValues_,
bytes[2] memory orderData_,
bytesmemory signature_
)
{
returnabi.decode(_encodedZeroExOrderArgs, (address[4], uint256[6], bytes[2], bytes));
}
/// @dev Parses the asset address from 0x assetDatafunction__getAssetAddress(bytesmemory _assetData)
privatepurereturns (address assetAddress_)
{
assembly {
assetAddress_ :=mload(add(_assetData, 36))
}
}
/// @dev Gets the 0x assetProxy address for an ERC20 tokenfunction__getAssetProxy(bytesmemory _assetData) privateviewreturns (address assetProxy_) {
bytes4 assetProxyId;
assembly {
assetProxyId :=and(
mload(add(_assetData, 32)),
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
)
}
assetProxy_ = IZeroExV2(EXCHANGE).getAssetProxy(assetProxyId);
}
/////////////////////////////// ALLOWED MAKERS REGISTRY ////////////////////////////////// @notice Adds accounts to the list of allowed 0x order makers/// @param _accountsToAdd Accounts to addfunctionaddAllowedMakers(address[] calldata _accountsToAdd) externalonlyFundDeployerOwner{
__addAllowedMakers(_accountsToAdd);
}
/// @notice Removes accounts from the list of allowed 0x order makers/// @param _accountsToRemove Accounts to removefunctionremoveAllowedMakers(address[] calldata _accountsToRemove)
externalonlyFundDeployerOwner{
require(_accountsToRemove.length>0, "removeAllowedMakers: Empty _accountsToRemove");
for (uint256 i; i < _accountsToRemove.length; i++) {
require(
isAllowedMaker(_accountsToRemove[i]),
"removeAllowedMakers: Account is not an allowed maker"
);
makerToIsAllowed[_accountsToRemove[i]] =false;
emit AllowedMakerRemoved(_accountsToRemove[i]);
}
}
/// @dev Helper to add accounts to the list of allowed makersfunction__addAllowedMakers(address[] memory _accountsToAdd) private{
require(_accountsToAdd.length>0, "__addAllowedMakers: Empty _accountsToAdd");
for (uint256 i; i < _accountsToAdd.length; i++) {
require(!isAllowedMaker(_accountsToAdd[i]), "__addAllowedMakers: Value already set");
makerToIsAllowed[_accountsToAdd[i]] =true;
emit AllowedMakerAdded(_accountsToAdd[i]);
}
}
///////////////////// STATE GETTERS //////////////////////// @notice Gets the `EXCHANGE` variable value/// @return exchange_ The `EXCHANGE` variable valuefunctiongetExchange() externalviewreturns (address exchange_) {
return EXCHANGE;
}
/// @notice Checks whether an account is an allowed maker of 0x orders/// @param _who The account to check/// @return isAllowedMaker_ True if _who is an allowed makerfunctionisAllowedMaker(address _who) publicviewreturns (bool isAllowedMaker_) {
return makerToIsAllowed[_who];
}
}