// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title AddressArray Library
/// @author Enzyme Council <security@enzyme.finance>
/// @notice A library to extend the address array data type
library AddressArrayLib {
/// @dev Helper to add an item to an array. Does not assert uniqueness of the new item.
function addItem(address[] memory _self, address _itemToAdd)
internal
pure
returns (address[] memory nextArray_)
{
nextArray_ = new address[](_self.length + 1);
for (uint256 i; i < _self.length; i++) {
nextArray_[i] = _self[i];
}
nextArray_[_self.length] = _itemToAdd;
return nextArray_;
}
/// @dev Helper to add an item to an array, only if it is not already in the array.
function addUniqueItem(address[] memory _self, address _itemToAdd)
internal
pure
returns (address[] memory nextArray_)
{
if (contains(_self, _itemToAdd)) {
return _self;
}
return addItem(_self, _itemToAdd);
}
/// @dev Helper to verify if an array contains a particular value
function contains(address[] memory _self, address _target)
internal
pure
returns (bool doesContain_)
{
for (uint256 i; i < _self.length; i++) {
if (_target == _self[i]) {
return true;
}
}
return false;
}
/// @dev Helper to reassign all items in an array with a specified value
function fill(address[] memory _self, address _value)
internal
pure
returns (address[] memory nextArray_)
{
nextArray_ = new address[](_self.length);
for (uint256 i; i < nextArray_.length; i++) {
nextArray_[i] = _value;
}
return nextArray_;
}
/// @dev Helper to verify if array is a set of unique values.
/// Does not assert length > 0.
function isUniqueSet(address[] memory _self) internal pure returns (bool isUnique_) {
if (_self.length <= 1) {
return true;
}
uint256 arrayLength = _self.length;
for (uint256 i; i < arrayLength; i++) {
for (uint256 j = i + 1; j < arrayLength; j++) {
if (_self[i] == _self[j]) {
return false;
}
}
}
return true;
}
/// @dev Helper to remove items from an array. Removes all matching occurrences of each item.
/// Does not assert uniqueness of either array.
function removeItems(address[] memory _self, address[] memory _itemsToRemove)
internal
pure
returns (address[] memory nextArray_)
{
if (_itemsToRemove.length == 0) {
return _self;
}
bool[] memory indexesToRemove = new bool[](_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;
} else if (remainingItemsCount > 0) {
nextArray_ = new address[](remainingItemsCount);
uint256 nextArrayIndex;
for (uint256 i; i < _self.length; i++) {
if (!indexesToRemove[i]) {
nextArray_[nextArrayIndex] = _self[i];
nextArrayIndex++;
}
}
}
return nextArray_;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.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}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _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 (string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
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}.
*/
function decimals() public view virtual returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (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`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @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`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @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.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @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`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @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) internal virtual {
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) internal virtual {
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) internal virtual {
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) internal virtual {
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 virtual {
_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(address from, address to, uint256 amount) internal virtual { }
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.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.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// 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.
struct Set {
// Storage of set values
bytes32[] _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) private returns (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;
return true;
} else {
return false;
}
}
/**
* @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) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 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 slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (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) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
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.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, 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.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (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}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {
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.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(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.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (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}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
// UintSet
struct UintSet {
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.
*/
function add(UintSet storage set, uint256 value) internal returns (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.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (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}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "../../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 extension
abstract contract ExtensionBase is IExtension {
mapping(address => address) internal comptrollerProxyToVaultProxy;
/// @notice Allows extension to run logic during fund activation
/// @dev Unimplemented by default, may be overridden.
function activateForFund(bool) external virtual override {
return;
}
/// @notice Allows extension to run logic during fund deactivation (destruct)
/// @dev Unimplemented by default, may be overridden.
function deactivateForFund() external virtual override {
return;
}
/// @notice Receives calls from ComptrollerLib.callOnExtension()
/// and dispatches the appropriate action
/// @dev Unimplemented by default, may be overridden.
function receiveCallFromComptroller(
address,
uint256,
bytes calldata
) external virtual override {
revert("receiveCallFromComptroller: Unimplemented for Extension");
}
/// @notice Allows extension to run logic during fund configuration
/// @dev Unimplemented by default, may be overridden.
function setConfigForFund(bytes calldata) external virtual override {
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)
internal
returns (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 fund
function getVaultProxyForFund(address _comptrollerProxy)
public
view
returns (address vaultProxy_)
{
return comptrollerProxyToVaultProxy[_comptrollerProxy];
}
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "../../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 FundDeployer
abstract contract FundDeployerOwnerMixin {
address internal immutable FUND_DEPLOYER;
modifier onlyFundDeployerOwner() {
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 contract
function getOwner() public view returns (address owner_) {
return IFundDeployer(FUND_DEPLOYER).getOwner();
}
///////////////////
// STATE GETTERS //
///////////////////
/// @notice Gets the `FUND_DEPLOYER` variable
/// @return fundDeployer_ The `FUND_DEPLOYER` variable value
function getFundDeployer() external view returns (address fundDeployer_) {
return FUND_DEPLOYER;
}
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IComptroller Interface
/// @author Enzyme Council <security@enzyme.finance>
interface IComptroller {
enum VaultAction {
None,
BurnShares,
MintShares,
TransferShares,
ApproveAssetSpender,
WithdrawAssetTo,
AddTrackedAsset,
RemoveTrackedAsset
}
function activate(address, bool) external;
function calcGav(bool) external returns (uint256, bool);
function calcGrossShareValue(bool) external returns (uint256, bool);
function callOnExtension(
address,
uint256,
bytes calldata
) external;
function configureExtensions(bytes calldata, bytes calldata) external;
function destruct() external;
function getDenominationAsset() external view returns (address);
function getVaultProxy() external view returns (address);
function init(address, uint256) external;
function permissionedVaultAction(VaultAction, bytes calldata) external;
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IDerivativePriceFeed Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Simple interface for derivative price source oracle implementations
interface IDerivativePriceFeed {
function calcUnderlyingValues(address, uint256)
external
returns (address[] memory, uint256[] memory);
function isSupportedAsset(address) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (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.
*/
function transfer(address recipient, uint256 amount) external returns (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.
*/
function allowance(address owner, address spender) external view returns (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.
*/
function approve(address spender, uint256 amount) external returns (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.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed 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.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IExtension Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for all extensions
interface IExtension {
function activateForFund(bool _isMigration) external;
function deactivateForFund() external;
function receiveCallFromComptroller(
address _comptrollerProxy,
uint256 _actionId,
bytes calldata _callArgs
) external;
function setConfigForFund(bytes calldata _configData) external;
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IFundDeployer Interface
/// @author Enzyme Council <security@enzyme.finance>
interface IFundDeployer {
enum ReleaseStatus {PreLaunch, Live, Paused}
function getOwner() external view returns (address);
function getReleaseStatus() external view returns (ReleaseStatus);
function isRegisteredVaultCall(address, bytes4) external view returns (bool);
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "../IIntegrationManager.sol";
/// @title Integration Adapter interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for all integration adapters
interface IIntegrationAdapter {
function identifier() external pure returns (string memory identifier_);
function parseAssetsForMethod(bytes4 _selector, bytes calldata _encodedCallArgs)
external
view
returns (
IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory spendAssetAmounts_,
address[] memory incomingAssets_,
uint256[] memory minIncomingAssetAmounts_
);
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IIntegrationManager interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for the IntegrationManager
interface IIntegrationManager {
enum SpendAssetsHandleType {None, Approve, Transfer, Remove}
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IMigratableVault Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @dev DO NOT EDIT CONTRACT
interface IMigratableVault {
function canMigrate(address _who) external view returns (bool canMigrate_);
function init(
address _owner,
address _accessor,
string calldata _fundName
) external;
function setAccessor(address _nextAccessor) external;
function setVaultLib(address _nextVaultLib) external;
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
/// @title PolicyManager Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for the PolicyManager
interface IPolicyManager {
enum PolicyHook {
BuySharesSetup,
PreBuyShares,
PostBuyShares,
BuySharesCompleted,
PreCallOnIntegration,
PostCallOnIntegration
}
function validatePolicies(
address,
PolicyHook,
bytes calldata
) external;
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
/// @title IPrimitivePriceFeed Interface
/// @author Enzyme Council <security@enzyme.finance>
/// @notice Interface for primitive price feeds
interface IPrimitivePriceFeed {
function calcCanonicalValue(
address,
uint256,
address
) external view returns (uint256, bool);
function calcLiveValue(
address,
uint256,
address
) external view returns (uint256, bool);
function isSupportedAsset(address) external view returns (bool);
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "../../../../persistent/utils/IMigratableVault.sol";
/// @title IVault Interface
/// @author Enzyme Council <security@enzyme.finance>
interface IVault is IMigratableVault {
function addTrackedAsset(address) external;
function approveAssetSpender(
address,
address,
uint256
) external;
function burnShares(address, uint256) external;
function callOnContract(address, bytes calldata) external;
function getAccessor() external view returns (address);
function getOwner() external view returns (address);
function getTrackedAssets() external view returns (address[] memory);
function isTrackedAsset(address) external view returns (bool);
function mintShares(address, uint256) external;
function removeTrackedAsset(address) external;
function transferShares(
address,
address,
uint256
) external;
function withdrawAssetTo(
address,
address,
uint256
) external;
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@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 "../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 funds
contract IntegrationManager is
IIntegrationManager,
ExtensionBase,
FundDeployerOwnerMixin,
PermissionedVaultActionMixin
{
using AddressArrayLib for address[];
using EnumerableSet for EnumerableSet.AddressSet;
using SafeMath for uint256;
event AdapterDeregistered(address indexed adapter, string indexed identifier);
event AdapterRegistered(address indexed adapter, string indexed identifier);
event AuthUserAddedForFund(address indexed comptrollerProxy, address indexed account);
event AuthUserRemovedForFund(address indexed comptrollerProxy, address indexed account);
event CallOnIntegrationExecutedForFund(
address indexed comptrollerProxy,
address vaultProxy,
address caller,
address indexed adapter,
bytes4 indexed selector,
bytes integrationData,
address[] incomingAssets,
uint256[] incomingAssetAmounts,
address[] outgoingAssets,
uint256[] outgoingAssetAmounts
);
address private immutable DERIVATIVE_PRICE_FEED;
address private immutable POLICY_MANAGER;
address private immutable PRIMITIVE_PRICE_FEED;
EnumerableSet.AddressSet private registeredAdapters;
mapping(address => mapping(address => bool)) private comptrollerProxyToAcctToIsAuthUser;
constructor(
address _fundDeployer,
address _policyManager,
address _derivativePriceFeed,
address _primitivePriceFeed
) public FundDeployerOwnerMixin(_fundDeployer) {
DERIVATIVE_PRICE_FEED = _derivativePriceFeed;
POLICY_MANAGER = _policyManager;
PRIMITIVE_PRICE_FEED = _primitivePriceFeed;
}
/////////////
// GENERAL //
/////////////
/// @notice Activates the extension by storing the VaultProxy
function activateForFund(bool) external override {
__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 authorize
function addAuthUserForFund(address _comptrollerProxy, address _who) external {
__validateSetAuthUser(_comptrollerProxy, _who, true);
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who] = true;
emit AuthUserAddedForFund(_comptrollerProxy, _who);
}
/// @notice Deactivate the extension by destroying storage
function deactivateForFund() external override {
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 remove
function removeAuthUserForFund(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 owner
function isAuthUserForFund(address _comptrollerProxy, address _who)
public
view
returns (bool isAuthUser_)
{
return
comptrollerProxyToAcctToIsAuthUser[_comptrollerProxy][_who] ||
_who == IVault(comptrollerProxyToVaultProxy[_comptrollerProxy]).getOwner();
}
/// @dev Helper to validate calls to update comptrollerProxyToAcctToIsAuthUser
function __validateSetAuthUser(
address _comptrollerProxy,
address _who,
bool _nextIsAuthUser
) private view {
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 action
function receiveCallFromComptroller(
address _caller,
uint256 _actionId,
bytes calldata _callArgs
) external override {
// Since we validate and store the ComptrollerProxy-VaultProxy pairing during
// activateForFund(), this function does not require further validation of the
// sending ComptrollerProxy
address 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 action
if (_actionId == 0) {
__callOnIntegration(_caller, vaultProxy, _callArgs);
} else if (_actionId == 1) {
__addZeroBalanceTrackedAssets(vaultProxy, _callArgs);
} else if (_actionId == 2) {
__removeZeroBalanceTrackedAssets(vaultProxy, _callArgs);
} else {
revert("receiveCallFromComptroller: Invalid _actionId");
}
}
/// @dev Adds assets with a zero balance as tracked assets of the fund
function __addZeroBalanceTrackedAssets(address _vaultProxy, bytes memory _callArgs) private {
address[] memory assets = abi.decode(_callArgs, (address[]));
for (uint256 i; i < assets.length; i++) {
require(
__isSupportedAsset(assets[i]),
"__addZeroBalanceTrackedAssets: Unsupported asset"
);
require(
ERC20(assets[i]).balanceOf(_vaultProxy) == 0,
"__addZeroBalanceTrackedAssets: Balance is not zero"
);
__addTrackedAsset(msg.sender, assets[i]);
}
}
/// @dev Removes assets with a zero balance from tracked assets of the fund
function __removeZeroBalanceTrackedAssets(address _vaultProxy, bytes memory _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(
ERC20(assets[i]).balanceOf(_vaultProxy) == 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,
bytes memory _callArgs
) private {
(
address adapter,
bytes4 selector,
bytes memory 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, bytes memory _callArgs)
private
returns (
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 args
function __decodeCallOnIntegrationArgs(bytes memory _callArgs)
private
pure
returns (
address adapter_,
bytes4 selector_,
bytes memory integrationData_
)
{
return abi.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,
bytes memory _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 error
function __executeCoI(
address _vaultProxy,
bytes memory _callArgs,
bytes memory _encodedAssetTransferArgs
) private {
(
address adapter,
bytes4 selector,
bytes memory integrationData
) = __decodeCallOnIntegrationArgs(_callArgs);
(bool success, bytes memory returnData) = adapter.call(
abi.encodeWithSelector(
selector,
_vaultProxy,
integrationData,
_encodedAssetTransferArgs
)
);
require(success, string(returnData));
}
/// @dev Helper to get the vault's balance of a particular asset
function __getVaultAssetBalance(address _vaultProxy, address _asset)
private
view
returns (uint256)
{
return ERC20(_asset).balanceOf(_vaultProxy);
}
/// @dev Helper to check if an asset is supported
function __isSupportedAsset(address _asset) private view returns (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 CoI
function __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 CoI
function __preProcessCoI(address _vaultProxy, bytes memory _callArgs)
private
returns (
address[] memory expectedIncomingAssets_,
uint256[] memory preCallIncomingAssetBalances_,
uint256[] memory minIncomingAssetAmounts_,
SpendAssetsHandleType spendAssetsHandleType_,
address[] memory spendAssets_,
uint256[] memory maxSpendAssetAmounts_,
uint256[] memory preCallSpendAssetBalances_
)
{
(
address adapter,
bytes4 selector,
bytes memory 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_ = new uint256[](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])) {
preCallIncomingAssetBalances_[i] = ERC20(expectedIncomingAssets_[i]).balanceOf(
_vaultProxy
);
}
}
// Get pre-call balances of spend assets and grant approvals to adapter
preCallSpendAssetBalances_ = new uint256[](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");
// If spend asset is also an incoming asset, no need to record its balance
if (!expectedIncomingAssets_.contains(spendAssets_[i])) {
preCallSpendAssetBalances_[i] = ERC20(spendAssets_[i]).balanceOf(_vaultProxy);
}
// 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]
);
} else if (spendAssetsHandleType_ == SpendAssetsHandleType.Transfer) {
__withdrawAssetTo(msg.sender, spendAssets_[i], adapter, maxSpendAssetAmounts_[i]);
} else if (spendAssetsHandleType_ == SpendAssetsHandleType.Remove) {
__removeTrackedAsset(msg.sender, spendAssets_[i]);
}
}
}
/// @dev Helper for the actions to take on external contracts after executing CoI
function __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 CoI
function __postProcessCoI(
address _vaultProxy,
address[] memory _expectedIncomingAssets,
uint256[] memory _preCallIncomingAssetBalances,
uint256[] memory _minIncomingAssetAmounts,
SpendAssetsHandleType _spendAssetsHandleType,
address[] memory _spendAssets,
uint256[] memory _maxSpendAssetAmounts,
uint256[] memory _preCallSpendAssetBalances
)
private
returns (
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
) private returns (address[] memory incomingAssets_, uint256[] memory incomingAssetAmounts_) {
// Incoming assets = expected incoming assets + spend assets with increased balances
uint256 incomingAssetsCount = _expectedIncomingAssets.length.add(
_increasedSpendAssets.length
);
// Calculate and validate incoming asset amounts
incomingAssets_ = new address[](incomingAssetsCount);
incomingAssetAmounts_ = new uint256[](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 vars
if (_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
)
private
returns (
address[] memory outgoingAssets_,
uint256[] memory outgoingAssetAmounts_,
address[] memory increasedSpendAssets_,
uint256[] memory increasedSpendAssetAmounts_
)
{
// Determine spend asset balance changes
uint256[] memory postCallSpendAssetBalances = new uint256[](_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 asset
if (_preCallSpendAssetBalances[i] == 0) {
continue;
}
// Handle SpendAssetsHandleType.Remove separately
if (_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 outgoing
if (postCallSpendAssetBalances[i] < _preCallSpendAssetBalances[i]) {
outgoingAssetsCount++;
} else if (postCallSpendAssetBalances[i] > _preCallSpendAssetBalances[i]) {
increasedSpendAssetsCount++;
}
}
// Format outgoingAssets and increasedSpendAssets (spend assets with unexpected increase in balance)
outgoingAssets_ = new address[](outgoingAssetsCount);
outgoingAssetAmounts_ = new uint256[](outgoingAssetsCount);
increasedSpendAssets_ = new address[](increasedSpendAssetsCount);
increasedSpendAssetAmounts_ = new uint256[](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 outgoing
if (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++;
} else if (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 deregistered
function deregisterAdapters(address[] calldata _adapters) external onlyFundDeployerOwner {
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 registered
function registerAdapters(address[] calldata _adapters) external onlyFundDeployerOwner {
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 registered
function adapterIsRegistered(address _adapter) public view returns (bool isRegistered_) {
return registeredAdapters.contains(_adapter);
}
/// @notice Gets the `DERIVATIVE_PRICE_FEED` variable
/// @return derivativePriceFeed_ The `DERIVATIVE_PRICE_FEED` variable value
function getDerivativePriceFeed() external view returns (address derivativePriceFeed_) {
return DERIVATIVE_PRICE_FEED;
}
/// @notice Gets the `POLICY_MANAGER` variable
/// @return policyManager_ The `POLICY_MANAGER` variable value
function getPolicyManager() external view returns (address policyManager_) {
return POLICY_MANAGER;
}
/// @notice Gets the `PRIMITIVE_PRICE_FEED` variable
/// @return primitivePriceFeed_ The `PRIMITIVE_PRICE_FEED` variable value
function getPrimitivePriceFeed() external view returns (address primitivePriceFeed_) {
return PRIMITIVE_PRICE_FEED;
}
/// @notice Gets all registered integration adapters
/// @return registeredAdaptersArray_ A list of all registered integration adapters
function getRegisteredAdapters()
external
view
returns (address[] memory registeredAdaptersArray_)
{
registeredAdaptersArray_ = new address[](registeredAdapters.length());
for (uint256 i = 0; i < registeredAdaptersArray_.length; i++) {
registeredAdaptersArray_[i] = registeredAdapters.at(i);
}
return registeredAdaptersArray_;
}
}
// SPDX-License-Identifier: GPL-3.0
/*
This file is part of the Enzyme Protocol.
(c) Enzyme Council <council@enzyme.finance>
For the full license information, please view the LICENSE
file that was distributed with this source code.
*/
pragma solidity 0.6.12;
import "../../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 calls
abstract contract PermissionedVaultActionMixin {
/// @notice Adds a tracked asset to the fund
/// @param _comptrollerProxy The ComptrollerProxy of the fund
/// @param _asset The asset to add
function __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 allowance
function __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 burn
function __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 mint
function __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 remove
function __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 transfer
function __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 withdraw
function __withdrawAssetTo(
address _comptrollerProxy,
address _asset,
address _target,
uint256 _amount
) internal {
IComptroller(_comptrollerProxy).permissionedVaultAction(
IComptroller.VaultAction.WithdrawAssetTo,
abi.encode(_asset, _target, _amount)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
{
"compilationTarget": {
"contracts/release/extensions/integration-manager/IntegrationManager.sol": "IntegrationManager"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"details": {
"constantOptimizer": true,
"cse": true,
"deduplicate": true,
"jumpdestRemover": true,
"orderLiterals": true,
"peephole": true,
"yul": false
},
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_fundDeployer","type":"address"},{"internalType":"address","name":"_policyManager","type":"address"},{"internalType":"address","name":"_derivativePriceFeed","type":"address"},{"internalType":"address","name":"_primitivePriceFeed","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":true,"internalType":"string","name":"identifier","type":"string"}],"name":"AdapterDeregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":true,"internalType":"string","name":"identifier","type":"string"}],"name":"AdapterRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AuthUserAddedForFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AuthUserRemovedForFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"comptrollerProxy","type":"address"},{"indexed":false,"internalType":"address","name":"vaultProxy","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":true,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":false,"internalType":"bytes","name":"integrationData","type":"bytes"},{"indexed":false,"internalType":"address[]","name":"incomingAssets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"incomingAssetAmounts","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"outgoingAssets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"outgoingAssetAmounts","type":"uint256[]"}],"name":"CallOnIntegrationExecutedForFund","type":"event"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"activateForFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_adapter","type":"address"}],"name":"adapterIsRegistered","outputs":[{"internalType":"bool","name":"isRegistered_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"_who","type":"address"}],"name":"addAuthUserForFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deactivateForFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"}],"name":"deregisterAdapters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDerivativePriceFeed","outputs":[{"internalType":"address","name":"derivativePriceFeed_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFundDeployer","outputs":[{"internalType":"address","name":"fundDeployer_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPolicyManager","outputs":[{"internalType":"address","name":"policyManager_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrimitivePriceFeed","outputs":[{"internalType":"address","name":"primitivePriceFeed_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRegisteredAdapters","outputs":[{"internalType":"address[]","name":"registeredAdaptersArray_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"}],"name":"getVaultProxyForFund","outputs":[{"internalType":"address","name":"vaultProxy_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"_who","type":"address"}],"name":"isAuthUserForFund","outputs":[{"internalType":"bool","name":"isAuthUser_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"uint256","name":"_actionId","type":"uint256"},{"internalType":"bytes","name":"_callArgs","type":"bytes"}],"name":"receiveCallFromComptroller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"}],"name":"registerAdapters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_comptrollerProxy","type":"address"},{"internalType":"address","name":"_who","type":"address"}],"name":"removeAuthUserForFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"setConfigForFund","outputs":[],"stateMutability":"nonpayable","type":"function"}]