Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 2 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
20974081 | 51 days ago | Contract Creation | 0 ETH | |||
20974081 | 51 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RoleManagerFactory
Compiler Version
v0.8.18+commit.87f61d96
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2024-10-15 */ // SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.18 ^0.8.0 ^0.8.1 ^0.8.18; // lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool); } // lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol) /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); } // lib/openzeppelin-contracts/contracts/utils/Address.sol // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) /** * @dev Collection of functions related to the address type */ library Address { /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (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._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "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._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { 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._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 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._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } // lib/openzeppelin-contracts/contracts/utils/Context.sol // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) /** * @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 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) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } } // lib/openzeppelin-contracts/contracts/utils/math/Math.sol // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } } // lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } } // lib/tokenized-strategy-periphery/src/utils/Clonable.sol contract Clonable { /// @notice Set to the address to auto clone from. address public original; /** * @notice Clone the contracts default `original` contract. * @return Address of the new Minimal Proxy clone. */ function _clone() internal virtual returns (address) { return _clone(original); } /** * @notice Clone any `_original` contract. * @return _newContract Address of the new Minimal Proxy clone. */ function _clone( address _original ) internal virtual returns (address _newContract) { // Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol bytes20 addressBytes = bytes20(_original); assembly { // EIP-1167 bytecode let clone_code := mload(0x40) mstore( clone_code, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 ) mstore(add(clone_code, 0x14), addressBytes) mstore( add(clone_code, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 ) _newContract := create(0, clone_code, 0x37) } } } // lib/tokenized-strategy-periphery/src/utils/Governance.sol contract Governance { /// @notice Emitted when the governance address is updated. event GovernanceTransferred( address indexed previousGovernance, address indexed newGovernance ); modifier onlyGovernance() { _checkGovernance(); _; } /// @notice Checks if the msg sender is the governance. function _checkGovernance() internal view virtual { require(governance == msg.sender, "!governance"); } /// @notice Address that can set the default base fee and provider address public governance; constructor(address _governance) { governance = _governance; emit GovernanceTransferred(address(0), _governance); } /** * @notice Sets a new address as the governance of the contract. * @dev Throws if the caller is not current governance. * @param _newGovernance The new governance address. */ function transferGovernance( address _newGovernance ) external virtual onlyGovernance { require(_newGovernance != address(0), "ZERO ADDRESS"); address oldGovernance = governance; governance = _newGovernance; emit GovernanceTransferred(oldGovernance, _newGovernance); } } // lib/yearn-vaults-v3/contracts/interfaces/Roles.sol // prettier-ignore library Roles { uint256 internal constant ADD_STRATEGY_MANAGER = 1; uint256 internal constant REVOKE_STRATEGY_MANAGER = 2; uint256 internal constant FORCE_REVOKE_MANAGER = 4; uint256 internal constant ACCOUNTANT_MANAGER = 8; uint256 internal constant QUEUE_MANAGER = 16; uint256 internal constant REPORTING_MANAGER = 32; uint256 internal constant DEBT_MANAGER = 64; uint256 internal constant MAX_DEBT_MANAGER = 128; uint256 internal constant DEPOSIT_LIMIT_MANAGER = 256; uint256 internal constant WITHDRAW_LIMIT_MANAGER = 512; uint256 internal constant MINIMUM_IDLE_MANAGER = 1024; uint256 internal constant PROFIT_UNLOCK_MANAGER = 2048; uint256 internal constant DEBT_PURCHASER = 4096; uint256 internal constant EMERGENCY_MANAGER = 8192; uint256 internal constant ALL = 16383; } // src/interfaces/IProtocolAddressProvider.sol interface IProtocolAddressProvider { event UpdatedAddress(bytes32 id, address oldAddress, address newAddress); function name() external view returns (string memory); function governance() external view returns (address); function pendingGovernance() external view returns (address); function transferGovernance(address _newGovernance) external; function acceptGovernance() external; function setAddress(bytes32 _id, address _address) external; function getAddress(bytes32 _id) external view returns (address); function setReplacement(address _replacement) external; function setRouter(address _router) external; function setKeeper(address _keeper) external; function setAprOracle(address _aprOracle) external; function setReleaseRegistry(address _releaseRegistry) external; function setBaseFeeProvider(address _baseFeeProvider) external; function setCommonReportTrigger(address _commonReportTrigger) external; function setAuctionFactory(address _auctionFactory) external; function setSplitterFactory(address _splitterFactory) external; function setRegistryFactory(address _registryFactory) external; function setDebtAllocatorFactory(address _debtAllocatorFactory) external; function setAccountantFactory(address _accountantFactory) external; function setRoleManagerFactory(address _roleManagerFactory) external; function getReplacement() external view returns (address); function getRouter() external view returns (address); function getKeeper() external view returns (address); function getAprOracle() external view returns (address); function getReleaseRegistry() external view returns (address); function getBaseFeeProvider() external view returns (address); function getCommonReportTrigger() external view returns (address); function getAuctionFactory() external view returns (address); function getSplitterFactory() external view returns (address); function getRegistryFactory() external view returns (address); function getDebtAllocatorFactory() external view returns (address); function getAccountantFactory() external view returns (address); function getRoleManagerFactory() external view returns (address); } // src/managers/Positions.sol contract Positions { /// @notice Emitted when a new address is set for a position. event UpdatePositionHolder( bytes32 indexed position, address indexed newAddress ); /// @notice Emitted when a new set of roles is set for a position event UpdatePositionRoles(bytes32 indexed position, uint256 newRoles); /// @notice Position struct struct Position { address holder; uint96 roles; } /// @notice Only allow position holder to call. modifier onlyPositionHolder(bytes32 _positionId) { _isPositionHolder(_positionId); _; } /// @notice Check if the msg sender is specified position holder. function _isPositionHolder(bytes32 _positionId) internal view virtual { require(msg.sender == getPositionHolder(_positionId), "!allowed"); } /// @notice Mapping of position ID to position information. mapping(bytes32 => Position) internal _positions; /** * @notice Setter function for updating a positions holder. */ function _setPositionHolder( bytes32 _position, address _newHolder ) internal virtual { _positions[_position].holder = _newHolder; emit UpdatePositionHolder(_position, _newHolder); } /** * @notice Setter function for updating a positions roles. */ function _setPositionRoles( bytes32 _position, uint256 _newRoles ) internal virtual { _positions[_position].roles = uint96(_newRoles); emit UpdatePositionRoles(_position, _newRoles); } /** * @notice Get the address and roles given to a specific position. * @param _positionId The position identifier. * @return The address that holds that position. * @return The roles given to the specified position. */ function getPosition( bytes32 _positionId ) public view virtual returns (address, uint256) { Position memory _position = _positions[_positionId]; return (_position.holder, uint256(_position.roles)); } /** * @notice Get the current address assigned to a specific position. * @param _positionId The position identifier. * @return The current address assigned to the specified position. */ function getPositionHolder( bytes32 _positionId ) public view virtual returns (address) { return _positions[_positionId].holder; } /** * @notice Get the current roles given to a specific position ID. * @param _positionId The position identifier. * @return The current roles given to the specified position ID. */ function getPositionRoles( bytes32 _positionId ) public view virtual returns (uint256) { return uint256(_positions[_positionId].roles); } } // lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } // lib/tokenized-strategy-periphery/src/utils/Governance2Step.sol contract Governance2Step is Governance { /// @notice Emitted when the pending governance address is set. event UpdatePendingGovernance(address indexed newPendingGovernance); /// @notice Address that is set to take over governance. address public pendingGovernance; constructor(address _governance) Governance(_governance) {} /** * @notice Sets a new address as the `pendingGovernance` of the contract. * @dev Throws if the caller is not current governance. * @param _newGovernance The new governance address. */ function transferGovernance( address _newGovernance ) external virtual override onlyGovernance { require(_newGovernance != address(0), "ZERO ADDRESS"); pendingGovernance = _newGovernance; emit UpdatePendingGovernance(_newGovernance); } /** * @notice Allows the `pendingGovernance` to accept the role. */ function acceptGovernance() external virtual { require(msg.sender == pendingGovernance, "!pending governance"); emit GovernanceTransferred(governance, msg.sender); governance = msg.sender; pendingGovernance = address(0); } } // lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol) /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * _Available since v4.7._ */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); } // lib/openzeppelin-contracts/contracts/utils/Strings.sol // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } } // src/registry/ReleaseRegistry.sol interface IFactory { function apiVersion() external view returns (string memory); } interface ITokenizedStrategy { function apiVersion() external view returns (string memory); } /** * @title YearnV3 Release Registry * @author yearn.finance * @notice * Used by Yearn Governance to track on chain all * releases of the V3 vaults by API Version. */ contract ReleaseRegistry is Governance2Step { event NewRelease( uint256 indexed releaseId, address indexed factory, address indexed tokenizedStrategy, string apiVersion ); string public constant name = "Yearn V3 Release Registry"; // The total number of releases that have been deployed uint256 public numReleases; // Mapping of release id starting at 0 to the address // of the corresponding factory for that release. mapping(uint256 => address) public factories; // Mapping of release id starting at 0 to the address // of the corresponding Tokenized Strategy for that release. mapping(uint256 => address) public tokenizedStrategies; // Mapping of the API version for a specific release to the // place in the order it was released. mapping(string => uint256) public releaseTargets; constructor(address _governance) Governance2Step(_governance) {} /** * @notice Returns the latest factory. * @return The address of the factory for the latest release. */ function latestFactory() external view virtual returns (address) { uint256 _numReleases = numReleases; if (_numReleases == 0) return address(0); return factories[numReleases - 1]; } /** * @notice Returns the latest tokenized strategy. * @return The address of the tokenized strategy for the latest release. */ function latestTokenizedStrategy() external view virtual returns (address) { uint256 _numReleases = numReleases; if (_numReleases == 0) return address(0); return tokenizedStrategies[numReleases - 1]; } /** * @notice Returns the api version of the latest release. * @return The api version of the latest release. */ function latestRelease() external view virtual returns (string memory) { uint256 _numReleases = numReleases; if (_numReleases == 0) return ""; return IFactory(factories[numReleases - 1]).apiVersion(); } /** * @notice Issue a new release using a deployed factory. * @dev Stores the factory address in `factories` and the release * target in `releaseTargets` with its associated API version. * * Throws if caller isn't `governance`. * Throws if the api version is the same as the previous release. * Throws if the factory does not have the same api version as the tokenized strategy. * Emits a `NewRelease` event. * * @param _factory The factory that will be used create new vaults. */ function newRelease( address _factory, address _tokenizedStrategy ) external virtual onlyGovernance { // Check if the release is different from the current one uint256 releaseId = numReleases; string memory apiVersion = IFactory(_factory).apiVersion(); string memory tokenizedStrategyApiVersion = ITokenizedStrategy( _tokenizedStrategy ).apiVersion(); require( keccak256(bytes(apiVersion)) == keccak256(bytes(tokenizedStrategyApiVersion)), "ReleaseRegistry: api version mismatch" ); if (releaseId > 0) { // Make sure this isn't the same as the last one require( keccak256( bytes(IFactory(factories[releaseId - 1]).apiVersion()) ) != keccak256(bytes(apiVersion)), "ReleaseRegistry: same api version" ); } // Update latest release. factories[releaseId] = _factory; tokenizedStrategies[releaseId] = _tokenizedStrategy; // Set the api to the target. releaseTargets[apiVersion] = releaseId; // Increase our number of releases. numReleases = releaseId + 1; // Log the release for external listeners emit NewRelease(releaseId, _factory, _tokenizedStrategy, apiVersion); } } // lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead 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, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override 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 default value returned by this function, unless * it's overridden. * * 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 override returns (uint8) { return 18; } /** * @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: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, 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}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, 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}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); 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) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + 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) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This 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: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, 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: * * - `account` 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 += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(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); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(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 Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @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 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 {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} } // lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.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. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, 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. */ function safeApprove(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' require( (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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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, bytes memory 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. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } } // lib/yearn-vaults-v3/contracts/interfaces/IVault.sol interface IVault is IERC4626 { // STRATEGY EVENTS event StrategyChanged(address indexed strategy, uint256 change_type); event StrategyReported( address indexed strategy, uint256 gain, uint256 loss, uint256 current_debt, uint256 protocol_fees, uint256 total_fees, uint256 total_refunds ); // DEBT MANAGEMENT EVENTS event DebtUpdated( address indexed strategy, uint256 current_debt, uint256 new_debt ); // ROLE UPDATES event RoleSet(address indexed account, uint256 role); event UpdateRoleManager(address indexed role_manager); event UpdateAccountant(address indexed accountant); event UpdateDefaultQueue(address[] new_default_queue); event UpdateUseDefaultQueue(bool use_default_queue); event UpdatedMaxDebtForStrategy( address indexed sender, address indexed strategy, uint256 new_debt ); event UpdateAutoAllocate(bool auto_allocate); event UpdateDepositLimit(uint256 deposit_limit); event UpdateMinimumTotalIdle(uint256 minimum_total_idle); event UpdateProfitMaxUnlockTime(uint256 profit_max_unlock_time); event DebtPurchased(address indexed strategy, uint256 amount); event Shutdown(); struct StrategyParams { uint256 activation; uint256 last_report; uint256 current_debt; uint256 max_debt; } function FACTORY() external view returns (uint256); function strategies(address) external view returns (StrategyParams memory); function default_queue(uint256) external view returns (address); function use_default_queue() external view returns (bool); function auto_allocate() external view returns (bool); function minimum_total_idle() external view returns (uint256); function deposit_limit() external view returns (uint256); function deposit_limit_module() external view returns (address); function withdraw_limit_module() external view returns (address); function accountant() external view returns (address); function roles(address) external view returns (uint256); function role_manager() external view returns (address); function future_role_manager() external view returns (address); function isShutdown() external view returns (bool); function nonces(address) external view returns (uint256); function initialize( address, string memory, string memory, address, uint256 ) external; function setName(string memory) external; function setSymbol(string memory) external; function set_accountant(address new_accountant) external; function set_default_queue(address[] memory new_default_queue) external; function set_use_default_queue(bool) external; function set_auto_allocate(bool) external; function set_deposit_limit(uint256 deposit_limit) external; function set_deposit_limit( uint256 deposit_limit, bool should_override ) external; function set_deposit_limit_module( address new_deposit_limit_module ) external; function set_deposit_limit_module( address new_deposit_limit_module, bool should_override ) external; function set_withdraw_limit_module( address new_withdraw_limit_module ) external; function set_minimum_total_idle(uint256 minimum_total_idle) external; function setProfitMaxUnlockTime( uint256 new_profit_max_unlock_time ) external; function set_role(address account, uint256 role) external; function add_role(address account, uint256 role) external; function remove_role(address account, uint256 role) external; function transfer_role_manager(address role_manager) external; function accept_role_manager() external; function unlockedShares() external view returns (uint256); function pricePerShare() external view returns (uint256); function get_default_queue() external view returns (address[] memory); function process_report( address strategy ) external returns (uint256, uint256); function buy_debt(address strategy, uint256 amount) external; function add_strategy(address new_strategy) external; function revoke_strategy(address strategy) external; function force_revoke_strategy(address strategy) external; function update_max_debt_for_strategy( address strategy, uint256 new_max_debt ) external; function update_debt( address strategy, uint256 target_debt ) external returns (uint256); function update_debt( address strategy, uint256 target_debt, uint256 max_loss ) external returns (uint256); function shutdown_vault() external; function totalIdle() external view returns (uint256); function totalDebt() external view returns (uint256); function apiVersion() external view returns (string memory); function assess_share_of_unrealised_losses( address strategy, uint256 assets_needed ) external view returns (uint256); function profitMaxUnlockTime() external view returns (uint256); function fullProfitUnlockDate() external view returns (uint256); function profitUnlockingRate() external view returns (uint256); function lastProfitUpdate() external view returns (uint256); //// NON-STANDARD ERC-4626 FUNCTIONS \\\\ function withdraw( uint256 assets, address receiver, address owner, uint256 max_loss ) external returns (uint256); function withdraw( uint256 assets, address receiver, address owner, uint256 max_loss, address[] memory strategies ) external returns (uint256); function redeem( uint256 shares, address receiver, address owner, uint256 max_loss ) external returns (uint256); function redeem( uint256 shares, address receiver, address owner, uint256 max_loss, address[] memory strategies ) external returns (uint256); function maxWithdraw( address owner, uint256 max_loss ) external view returns (uint256); function maxWithdraw( address owner, uint256 max_loss, address[] memory strategies ) external view returns (uint256); function maxRedeem( address owner, uint256 max_loss ) external view returns (uint256); function maxRedeem( address owner, uint256 max_loss, address[] memory strategies ) external view returns (uint256); //// NON-STANDARD ERC-20 FUNCTIONS \\\\ function DOMAIN_SEPARATOR() external view returns (bytes32); function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (bool); } // lib/yearn-vaults-v3/contracts/interfaces/IVaultFactory.sol interface IVaultFactory_0 { event NewVault(address indexed vaultAddress, address indexed asset); event UpdateProtocolFeeBps( uint16 oldProtocolFeeBps, uint16 newProtocolFeeBps ); event UpdateProtocolFeeRecipient( address oldProtocolFeeRecipient, address newProtocolFeeRecipient ); event UpdateCustomProtocolFee(address vault, uint16 newCustomProtocolFee); event RemovedCustomProtocolFee(address vault); event FactoryShutdown(); event UpdatePendingGovernance(address newPendingGovernance); event GovernanceTransferred( address previousGovernance, address newGovernance ); function shutdown() external view returns (bool); function governance() external view returns (address); function pendingGovernance() external view returns (address); function name() external view returns (string memory); function use_custom_protocol_fee(address) external view returns (bool); function deploy_new_vault( address asset, string memory name, string memory symbol, address role_manager, uint256 profit_max_unlock_time ) external returns (address); function vault_original() external view returns (address); function apiVersion() external view returns (string memory); function protocol_fee_config() external view returns (uint16 fee_bps, address fee_recipient); function protocol_fee_config( address vault ) external view returns (uint16 fee_bps, address fee_recipient); function set_protocol_fee_bps(uint16 new_protocol_fee_bps) external; function set_protocol_fee_recipient( address new_protocol_fee_recipient ) external; function set_custom_protocol_fee_bps( address vault, uint16 new_custom_protocol_fee ) external; function remove_custom_protocol_fee(address vault) external; function shutdown_factory() external; function transferGovernance(address new_governance) external; function acceptGovernance() external; } // src/debtAllocators/DebtAllocator.sol interface IBaseFee { function basefee_global() external view returns (uint256); } /** * @title YearnV3 Debt Allocator * @author yearn.finance * @notice * This Debt Allocator is meant to be used alongside * Yearn V3 vaults to provide the needed triggers for a keeper * to perform automated debt updates for the vaults strategies. * * @dev * Each vault that should be managed by this allocator will * need to be added by first setting a `minimumChange` for the * vault, which will act as the minimum amount of funds to move that will * trigger a debt update. Then adding each strategy by setting a * `targetRatio` and optionally a `maxRatio`. * * The allocator aims to allocate debt between the strategies * based on their set target ratios. Which are denominated in basis * points and represent the percent of total assets that specific * strategy should hold (i.e 1_000 == 10% of the vaults `totalAssets`). * * The trigger will attempt to allocate up to the `maxRatio` when * the strategy has `minimumChange` amount less than the `targetRatio`. * And will pull funds to the `targetRatio` when it has `minimumChange` * more than its `maxRatio`. */ contract DebtAllocator is Governance { /// @notice An event emitted when the base fee provider is set. event UpdatedBaseFeeProvider(address baseFeeProvider); /// @notice An event emitted when a keeper is added or removed. event UpdateKeeper(address indexed keeper, bool allowed); /// @notice An event emitted when the max base fee is updated. event UpdateMaxAcceptableBaseFee(uint256 newMaxAcceptableBaseFee); /// @notice An event emitted when a strategies debt ratios are Updated. event UpdateStrategyDebtRatio( address indexed vault, address indexed strategy, uint256 newTargetRatio, uint256 newMaxRatio, uint256 newTotalDebtRatio ); /// @notice An event emitted when a strategy is added or removed. event StrategyChanged( address indexed vault, address indexed strategy, Status status ); /// @notice An event emitted when the minimum change is updated. event UpdateMinimumChange(address indexed vault, uint256 newMinimumChange); /// @notice An even emitted when the paused status is updated. event UpdatePaused(address indexed vault, bool indexed status); /// @notice An event emitted when the minimum time to wait is updated. event UpdateMinimumWait(uint256 newMinimumWait); /// @notice An event emitted when a keeper is added or removed. event UpdateManager(address indexed manager, bool allowed); /// @notice An event emitted when the max debt update loss is updated. event UpdateMaxDebtUpdateLoss(uint256 newMaxDebtUpdateLoss); /// @notice Status when a strategy is added or removed from the allocator. enum Status { NULL, ADDED, REMOVED } /// @notice Struct for each strategies info. struct StrategyConfig { // Flag to set when a strategy is added. bool added; // The ideal percent in Basis Points the strategy should have. uint16 targetRatio; // The max percent of assets the strategy should hold. uint16 maxRatio; // Timestamp of the last time debt was updated. // The debt updates must be done through this allocator // for this to be used. uint96 lastUpdate; // We have an extra 120 bits in the slot. // So we declare the variable in the struct so it can be // used if this contract is inherited. uint120 open; } /// @notice Struct to hold the vault's info. struct VaultConfig { // Optional flag to stop the triggers. bool paused; // The minimum amount denominated in asset that will // need to be moved to trigger a debt update. uint128 minimumChange; // Total debt ratio currently allocated in basis points. // Can't be more than 10_000. uint16 totalDebtRatio; } /// @notice Used during the `shouldUpdateDebt` to hold the data. struct StrategyDebtInfo { VaultConfig vaultConfig; StrategyConfig strategyConfig; uint256 vaultAssets; uint256 targetDebt; uint256 maxDebt; uint256 currentIdle; uint256 minIdle; uint256 toChange; } /// @notice Make sure the caller is governance or a manager. modifier onlyManagers() { _isManager(); _; } /// @notice Make sure the caller is a keeper modifier onlyKeepers() { _isKeeper(); _; } /// @notice Check is either factories governance or local manager. function _isManager() internal view virtual { require(managers[msg.sender] || msg.sender == governance, "!manager"); } /// @notice Check is one of the allowed keepers. function _isKeeper() internal view virtual { require(keepers[msg.sender], "!keeper"); } uint256 internal constant MAX_BPS = 10_000; /// @notice Time to wait between debt updates in seconds. uint256 public minimumWait; /// @notice Provider to read current block's base fee. address public baseFeeProvider; /// @notice Max loss to accept on debt updates in basis points. uint256 public maxDebtUpdateLoss; /// @notice Max the chains base fee can be during debt update. // Will default to max uint256 and need to be set to be used. uint256 public maxAcceptableBaseFee; /// @notice Mapping of addresses that are allowed to update debt. mapping(address => bool) public keepers; /// @notice Mapping of addresses that are allowed to update debt ratios. mapping(address => bool) public managers; mapping(address => VaultConfig) internal _vaultConfigs; /// @notice Mapping of vault => strategy => its config. mapping(address => mapping(address => StrategyConfig)) internal _strategyConfigs; constructor() Governance(msg.sender) {} /** * @notice Initialize the contract after being cloned. * @dev Sets default values for the global variables. */ function initialize(address _governance) external { require(governance == address(0), "initialized"); require(_governance != address(0), "ZERO ADDRESS"); governance = _governance; emit GovernanceTransferred(address(0), _governance); // Default max base fee to uint max. maxAcceptableBaseFee = type(uint256).max; // Default to allow 1 BP loss. maxDebtUpdateLoss = 1; // Default minimum wait to 6 hours minimumWait = 60 * 60 * 6; // Default to allow governance to be a keeper. keepers[_governance] = true; emit UpdateKeeper(_governance, true); } /** * @notice Debt update wrapper for the vault. * @dev This contract must have the DEBT_MANAGER role assigned to them. * * This will also uses the `maxUpdateDebtLoss` during debt * updates to assure decreases did not realize profits outside * of the allowed range. */ function update_debt( address _vault, address _strategy, uint256 _targetDebt ) public virtual onlyKeepers { // If going to 0 record full balance first. if (_targetDebt == 0) { IVault(_vault).process_report(_strategy); } // Update debt with the default max loss. IVault(_vault).update_debt(_strategy, _targetDebt, maxDebtUpdateLoss); // Update the last time the strategies debt was updated. _strategyConfigs[_vault][_strategy].lastUpdate = uint96( block.timestamp ); } /** * @notice Check if a strategy's debt should be updated. * @dev This should be called by a keeper to decide if a strategies * debt should be updated and if so by how much. * * @param _vault Address of the vault to update. * @param _strategy Address of the strategy to check. * @return . Bool representing if the debt should be updated. * @return . Calldata if `true` or reason if `false`. */ function shouldUpdateDebt( address _vault, address _strategy ) public view virtual returns (bool, bytes memory) { // Store all local variables in a struct to avoid stack to deep StrategyDebtInfo memory strategyDebtInfo; strategyDebtInfo.vaultConfig = getVaultConfig(_vault); // Don't do anything if paused. if (strategyDebtInfo.vaultConfig.paused) { return (false, bytes("Paused")); } // Check the base fee isn't too high. if (!isCurrentBaseFeeAcceptable()) return (false, bytes("Base Fee")); // Get the strategy specific debt config. strategyDebtInfo.strategyConfig = getStrategyConfig(_vault, _strategy); // Make sure the strategy has been added to the allocator. if (!strategyDebtInfo.strategyConfig.added) { return (false, bytes("!added")); } if ( block.timestamp - strategyDebtInfo.strategyConfig.lastUpdate <= minimumWait ) { return (false, bytes("min wait")); } // Retrieve the strategy specific parameters. IVault.StrategyParams memory params = IVault(_vault).strategies( _strategy ); // Make sure its an active strategy. require(params.activation != 0, "!active"); strategyDebtInfo.vaultAssets = IVault(_vault).totalAssets(); // Get the target debt for the strategy based on vault assets. strategyDebtInfo.targetDebt = Math.min( (strategyDebtInfo.vaultAssets * strategyDebtInfo.strategyConfig.targetRatio) / MAX_BPS, // Make sure it is not more than the max allowed. params.max_debt ); // Get the max debt we would want the strategy to have. strategyDebtInfo.maxDebt = Math.min( (strategyDebtInfo.vaultAssets * strategyDebtInfo.strategyConfig.maxRatio) / MAX_BPS, // Make sure it is not more than the max allowed. params.max_debt ); // If we need to add more. if (strategyDebtInfo.targetDebt > params.current_debt) { strategyDebtInfo.currentIdle = IVault(_vault).totalIdle(); strategyDebtInfo.minIdle = IVault(_vault).minimum_total_idle(); // We can't add more than the available idle. if (strategyDebtInfo.minIdle >= strategyDebtInfo.currentIdle) { return (false, bytes("No Idle")); } // Add up to the max if possible strategyDebtInfo.toChange = Math.min( strategyDebtInfo.maxDebt - params.current_debt, // Can't take more than is available. Math.min( strategyDebtInfo.currentIdle - strategyDebtInfo.minIdle, IVault(_strategy).maxDeposit(_vault) ) ); // If the amount to add is over our threshold. if ( strategyDebtInfo.toChange > strategyDebtInfo.vaultConfig.minimumChange ) { // Return true and the calldata. return ( true, abi.encodeCall( this.update_debt, ( _vault, _strategy, params.current_debt + strategyDebtInfo.toChange ) ) ); } // If current debt is greater than our max. } else if (strategyDebtInfo.maxDebt < params.current_debt) { strategyDebtInfo.toChange = params.current_debt - strategyDebtInfo.targetDebt; strategyDebtInfo.currentIdle = IVault(_vault).totalIdle(); strategyDebtInfo.minIdle = IVault(_vault).minimum_total_idle(); if (strategyDebtInfo.minIdle > strategyDebtInfo.currentIdle) { // Pull at least the amount needed for minIdle. strategyDebtInfo.toChange = Math.max( strategyDebtInfo.toChange, strategyDebtInfo.minIdle - strategyDebtInfo.currentIdle ); } // Find out by how much. Aim for the target. strategyDebtInfo.toChange = Math.min( strategyDebtInfo.toChange, // Account for the current liquidity constraints. // Use max redeem to match vault logic. IVault(_strategy).convertToAssets( IVault(_strategy).maxRedeem(_vault) ) ); // Check if it's over the threshold. if ( strategyDebtInfo.toChange > strategyDebtInfo.vaultConfig.minimumChange ) { // Can't lower debt if there are unrealised losses. if ( IVault(_vault).assess_share_of_unrealised_losses( _strategy, params.current_debt ) != 0 ) { return (false, bytes("unrealised loss")); } // If so return true and the calldata. return ( true, abi.encodeCall( this.update_debt, ( _vault, _strategy, params.current_debt - strategyDebtInfo.toChange ) ) ); } } // Either no change or below our minimumChange. return (false, bytes("Below Min")); } /*////////////////////////////////////////////////////////////// STRATEGY MANAGEMENT //////////////////////////////////////////////////////////////*/ /** * @notice Increase a strategies target debt ratio. * @dev `setStrategyDebtRatio` functions will do all needed checks. * @param _strategy The address of the strategy to increase the debt ratio for. * @param _increase The amount in Basis Points to increase it. */ function increaseStrategyDebtRatio( address _vault, address _strategy, uint256 _increase ) external virtual { setStrategyDebtRatio( _vault, _strategy, getStrategyTargetRatio(_vault, _strategy) + _increase ); } /** * @notice Decrease a strategies target debt ratio. * @param _strategy The address of the strategy to decrease the debt ratio for. * @param _decrease The amount in Basis Points to decrease it. */ function decreaseStrategyDebtRatio( address _vault, address _strategy, uint256 _decrease ) external virtual { setStrategyDebtRatio( _vault, _strategy, getStrategyTargetRatio(_vault, _strategy) - _decrease ); } /** * @notice Sets a new target debt ratio for a strategy. * @dev This will default to a 20% increase for max debt. * * @param _strategy Address of the strategy to set. * @param _targetRatio Amount in Basis points to allocate. */ function setStrategyDebtRatio( address _vault, address _strategy, uint256 _targetRatio ) public virtual { uint256 maxRatio = Math.min((_targetRatio * 12_000) / MAX_BPS, MAX_BPS); setStrategyDebtRatio(_vault, _strategy, _targetRatio, maxRatio); } /** * @notice Sets a new target debt ratio for a strategy. * @dev A `minimumChange` for that strategy must be set first. * This is to prevent debt from being updated too frequently. * * @param _vault Address of the vault * @param _strategy Address of the strategy to set. * @param _targetRatio Amount in Basis points to allocate. * @param _maxRatio Max ratio to give on debt increases. */ function setStrategyDebtRatio( address _vault, address _strategy, uint256 _targetRatio, uint256 _maxRatio ) public virtual onlyManagers { VaultConfig memory vaultConfig = getVaultConfig(_vault); // Make sure a minimumChange has been set. require(vaultConfig.minimumChange != 0, "!minimum"); // Cannot be more than 100%. require(_maxRatio <= MAX_BPS, "max too high"); // Max cannot be lower than the target. require(_maxRatio >= _targetRatio, "max ratio"); // Get the current config. StrategyConfig memory strategyConfig = getStrategyConfig( _vault, _strategy ); // Set added flag if not set yet. if (!strategyConfig.added) { strategyConfig.added = true; emit StrategyChanged(_vault, _strategy, Status.ADDED); } // Get what will be the new total debt ratio. uint256 newTotalDebtRatio = vaultConfig.totalDebtRatio - strategyConfig.targetRatio + _targetRatio; // Make sure it is under 100% allocated require(newTotalDebtRatio <= MAX_BPS, "ratio too high"); // Update local config. strategyConfig.targetRatio = uint16(_targetRatio); strategyConfig.maxRatio = uint16(_maxRatio); // Write to storage. _strategyConfigs[_vault][_strategy] = strategyConfig; _vaultConfigs[_vault].totalDebtRatio = uint16(newTotalDebtRatio); emit UpdateStrategyDebtRatio( _vault, _strategy, _targetRatio, _maxRatio, newTotalDebtRatio ); } /** * @notice Remove a strategy from this debt allocator. * @dev Will delete the full config for the strategy * @param _vault Address of the vault * @param _strategy Address of the address ro remove. */ function removeStrategy( address _vault, address _strategy ) external virtual onlyManagers { StrategyConfig memory strategyConfig = getStrategyConfig( _vault, _strategy ); require(strategyConfig.added, "!added"); uint256 target = strategyConfig.targetRatio; // Remove any debt ratio the strategy holds. if (target != 0) { uint256 newRatio = _vaultConfigs[_vault].totalDebtRatio - target; _vaultConfigs[_vault].totalDebtRatio = uint16(newRatio); emit UpdateStrategyDebtRatio(_vault, _strategy, 0, 0, newRatio); } // Remove the full config including the `added` flag. delete _strategyConfigs[_vault][_strategy]; // Emit Event. emit StrategyChanged(_vault, _strategy, Status.REMOVED); } /*////////////////////////////////////////////////////////////// VAULT MANAGEMENT //////////////////////////////////////////////////////////////*/ /** * @notice Set the minimum change variable for a strategy. * @dev This is the minimum amount of debt to be * added or pulled for it to trigger an update. * * @param _vault Address of the vault * @param _minimumChange The new minimum to set for the strategy. */ function setMinimumChange( address _vault, uint256 _minimumChange ) external virtual onlyGovernance { require(_minimumChange > 0, "zero change"); // Make sure it fits in the slot size. require(_minimumChange < type(uint128).max, "too high"); // Set the new minimum. _vaultConfigs[_vault].minimumChange = uint128(_minimumChange); emit UpdateMinimumChange(_vault, _minimumChange); } /** * @notice Allows governance to pause the triggers. * @param _vault Address of the vault * @param _status Status to set the `paused` bool to. */ function setPaused( address _vault, bool _status ) external virtual onlyGovernance { require(_status != _vaultConfigs[_vault].paused, "already set"); _vaultConfigs[_vault].paused = _status; emit UpdatePaused(_vault, _status); } /*////////////////////////////////////////////////////////////// ALLOCATOR MANAGEMENT //////////////////////////////////////////////////////////////*/ /** * @notice Set the minimum time to wait before re-updating a strategies debt. * @dev This is only enforced per strategy. * @param _minimumWait The minimum time in seconds to wait. */ function setMinimumWait( uint256 _minimumWait ) external virtual onlyGovernance { minimumWait = _minimumWait; emit UpdateMinimumWait(_minimumWait); } /** * @notice Set if a manager can update ratios. * @param _address The address to set mapping for. * @param _allowed If the address can call {update_debt}. */ function setManager( address _address, bool _allowed ) external virtual onlyGovernance { managers[_address] = _allowed; emit UpdateManager(_address, _allowed); } /** * @notice Set the max loss in Basis points to allow on debt updates. * @dev Withdrawing during debt updates use {redeem} which allows for 100% loss. * This can be used to assure a loss is not realized on redeem outside the tolerance. * @param _maxDebtUpdateLoss The max loss to accept on debt updates. */ function setMaxDebtUpdateLoss( uint256 _maxDebtUpdateLoss ) external virtual onlyGovernance { require(_maxDebtUpdateLoss <= MAX_BPS, "higher than max"); maxDebtUpdateLoss = _maxDebtUpdateLoss; emit UpdateMaxDebtUpdateLoss(_maxDebtUpdateLoss); } /** * @notice * Used to set our baseFeeProvider, which checks the network's current base * fee price to determine whether it is an optimal time to harvest or tend. * * This may only be called by governance. * @param _baseFeeProvider Address of our baseFeeProvider */ function setBaseFeeProvider( address _baseFeeProvider ) external virtual onlyGovernance { baseFeeProvider = _baseFeeProvider; emit UpdatedBaseFeeProvider(_baseFeeProvider); } /** * @notice Set the max acceptable base fee. * @dev This defaults to max uint256 and will need to * be set for it to be used. * * Is denominated in gwei. So 50gwei would be set as 50e9. * * @param _maxAcceptableBaseFee The new max base fee. */ function setMaxAcceptableBaseFee( uint256 _maxAcceptableBaseFee ) external virtual onlyGovernance { maxAcceptableBaseFee = _maxAcceptableBaseFee; emit UpdateMaxAcceptableBaseFee(_maxAcceptableBaseFee); } /** * @notice Set if a keeper can update debt. * @param _address The address to set mapping for. * @param _allowed If the address can call {update_debt}. */ function setKeeper( address _address, bool _allowed ) external virtual onlyGovernance { keepers[_address] = _allowed; emit UpdateKeeper(_address, _allowed); } /** * @notice Get a strategies full config. * @dev Used for customizations by inheriting the contract. * @param _vault Address of the vault * @param _strategy Address of the strategy. * @return The strategies current Config. */ function getStrategyConfig( address _vault, address _strategy ) public view virtual returns (StrategyConfig memory) { return _strategyConfigs[_vault][_strategy]; } /** * @notice Get a vaults full config. * @dev Used for customizations by inheriting the contract. * @param _vault Address of the vault. * @return The vaults current Config. */ function getVaultConfig( address _vault ) public view virtual returns (VaultConfig memory) { return _vaultConfigs[_vault]; } /** * @notice Get a vaults current total debt. * @param _vault Address of the vault */ function totalDebtRatio( address _vault ) external view virtual returns (uint256) { return getVaultConfig(_vault).totalDebtRatio; } /** * @notice Get a vaults minimum change required. * @param _vault Address of the vault */ function minimumChange( address _vault ) external view virtual returns (uint256) { return getVaultConfig(_vault).minimumChange; } /** * @notice Get the paused status of a vault * @param _vault Address of the vault */ function isPaused(address _vault) public view virtual returns (bool) { return getVaultConfig(_vault).paused; } /** * @notice Get a strategies target debt ratio. * @param _vault Address of the vault * @param _strategy Address of the strategy. * @return The strategies current targetRatio. */ function getStrategyTargetRatio( address _vault, address _strategy ) public view virtual returns (uint256) { return getStrategyConfig(_vault, _strategy).targetRatio; } /** * @notice Get a strategies max debt ratio. * @param _vault Address of the vault * @param _strategy Address of the strategy. * @return The strategies current maxRatio. */ function getStrategyMaxRatio( address _vault, address _strategy ) public view virtual returns (uint256) { return getStrategyConfig(_vault, _strategy).maxRatio; } /** * @notice Returns wether or not the current base fee is acceptable * based on the `maxAcceptableBaseFee`. * @return . If the current base fee is acceptable. */ function isCurrentBaseFeeAcceptable() public view virtual returns (bool) { address _baseFeeProvider = baseFeeProvider; if (_baseFeeProvider == address(0)) return true; return maxAcceptableBaseFee >= IBaseFee(_baseFeeProvider).basefee_global(); } } // src/registry/Registry.sol interface IVaultFactory_1 { function deploy_new_vault( address asset, string memory name, string memory symbol, address role_manager, uint256 profit_max_unlock_time ) external returns (address); function apiVersion() external view returns (string memory); } /** * @title YearnV3 Registry * @author yearn.finance * @notice * Serves as an on chain registry to track any Yearn V3 * vaults and strategies that a certain party wants to * endorse. * * Can also be used to deploy new vaults of any specific * API version. */ contract Registry is Governance { /// @notice Emitted when a new vault is deployed or added. event NewEndorsedVault( address indexed vault, address indexed asset, uint256 releaseVersion, uint256 vaultType ); /// @notice Emitted when a vault is removed. event RemovedVault( address indexed vault, address indexed asset, uint256 releaseVersion, uint256 vaultType ); /// @notice Emitted when a vault is tagged with a string. event VaultTagged(address indexed vault); /// @notice Emitted when gov adds ore removes a `tagger`. event UpdateTagger(address indexed account, bool status); /// @notice Emitted when gov adds ore removes a `endorser`. event UpdateEndorser(address indexed account, bool status); /// @notice Can only be gov or an `endorser`. modifier onlyEndorsers() { _isEndorser(); _; } /// @notice Can only be gov or a `tagger`. modifier onlyTaggers() { _isTagger(); _; } /// @notice Check is gov or an `endorser`. function _isEndorser() internal view { require(msg.sender == governance || endorsers[msg.sender], "!endorser"); } /// @notice Check is gov or a `tagger`. function _isTagger() internal view { require(msg.sender == governance || taggers[msg.sender], "!tagger"); } // Struct stored for every endorsed vault or strategy for // off chain use to easily retrieve info. struct Info { // The token thats being used. address asset; // The release number corresponding to the release registries version. uint96 releaseVersion; // Type of vault. uint64 vaultType; // Time when the vault was deployed for easier indexing. uint128 deploymentTimestamp; // Index the vault is at in array for easy removals. uint64 index; // String so that management can tag a vault with any info for FE's. string tag; } // Address used to get the specific versions from. address public immutable releaseRegistry; // Default type used for Multi strategy "Allocator" vaults. uint256 public constant MULTI_STRATEGY_TYPE = 1; // Default type used for Single "Tokenized" Strategy vaults. uint256 public constant SINGLE_STRATEGY_TYPE = 2; // Custom name for this Registry. string public name; // Old version of the registry to fall back to if exists. address public legacyRegistry; // Mapping for any address that is allowed to tag a vault. mapping(address => bool) public taggers; // Mapping for any address that is allowed to deploy or endorse. mapping(address => bool) public endorsers; // vault/strategy address => Info struct. mapping(address => Info) public vaultInfo; // Mapping to check if a specific `asset` has a vault. mapping(address => bool) public assetIsUsed; // asset => array of all endorsed vaults. mapping(address => address[]) internal _endorsedVaults; // Array of all tokens used as the underlying. address[] public assets; /** * @param _governance Address to set as owner of the Registry. * @param _name The custom string for this custom registry to be called. * @param _releaseRegistry The Permissionless releaseRegistry to deploy vaults through. */ constructor( address _governance, string memory _name, address _releaseRegistry ) Governance(_governance) { // Set name. name = _name; // Set releaseRegistry. releaseRegistry = _releaseRegistry; } /** * @notice Returns the total number of assets being used as the underlying. * @return The amount of assets. */ function numAssets() external view virtual returns (uint256) { return assets.length; } /** * @notice Get the full array of tokens being used. * @return The full array of underlying tokens being used/. */ function getAssets() external view virtual returns (address[] memory) { return assets; } /** * @notice The amount of endorsed vaults for a specific token. * @return The amount of endorsed vaults. */ function numEndorsedVaults( address _asset ) public view virtual returns (uint256) { return _endorsedVaults[_asset].length; } /** * @notice Get the array of vaults endorsed for an `_asset`. * @param _asset The underlying token used by the vaults. * @return The endorsed vaults. */ function getEndorsedVaults( address _asset ) external view virtual returns (address[] memory) { return _endorsedVaults[_asset]; } /** * @notice Get all endorsed vaults deployed using the Registry. * @dev This will return a nested array of all vaults deployed * separated by their underlying asset. * * This is only meant for off chain viewing and should not be used during any * on chain tx's. * * @return allEndorsedVaults A nested array containing all vaults. */ function getAllEndorsedVaults() external view virtual returns (address[][] memory allEndorsedVaults) { address[] memory allAssets = assets; uint256 length = assets.length; allEndorsedVaults = new address[][](length); for (uint256 i; i < length; ++i) { allEndorsedVaults[i] = _endorsedVaults[allAssets[i]]; } } /** * @notice Check if a vault is endorsed in this registry. * @dev This will check if the `asset` variable in the struct has been * set for an easy external view check. * @param _vault Address of the vault to check. * @return . The vaults endorsement status. */ function isEndorsed(address _vault) external view virtual returns (bool) { return vaultInfo[_vault].asset != address(0) || isLegacyVault(_vault); } /** * @notice Check if a vault is endorsed in the legacy registry. * @param _vault The vault to check. * @return True if the vault is endorsed in the legacy registry, false otherwise. */ function isLegacyVault(address _vault) public view virtual returns (bool) { address _legacy = legacyRegistry; if (_legacy == address(0)) return false; return Registry(_legacy).isEndorsed(_vault); } /** * @notice * Create and endorse a new multi strategy "Allocator" * vault and endorse it in this registry. * @dev * Throws if caller isn't `owner`. * Throws if no releases are registered yet. * Emits a `NewEndorsedVault` event. * @param _asset The asset that may be deposited into the new Vault. * @param _name Specify a custom Vault name. . * @param _symbol Specify a custom Vault symbol name. * @param _roleManager The address authorized for guardian interactions in the new Vault. * @param _profitMaxUnlockTime The time strategy profits will unlock over. * @return _vault address of the newly-deployed vault */ function newEndorsedVault( address _asset, string memory _name, string memory _symbol, address _roleManager, uint256 _profitMaxUnlockTime ) public virtual returns (address _vault) { return newEndorsedVault( _asset, _name, _symbol, _roleManager, _profitMaxUnlockTime, 0 // Default to latest version. ); } /** * @notice * Create and endorse a new multi strategy "Allocator" * vault and endorse it in this registry. * @dev * Throws if caller isn't `owner`. * Throws if no releases are registered yet. * Emits a `NewEndorsedVault` event. * @param _asset The asset that may be deposited into the new Vault. * @param _name Specify a custom Vault name. . * @param _symbol Specify a custom Vault symbol name. * @param _roleManager The address authorized for guardian interactions in the new Vault. * @param _profitMaxUnlockTime The time strategy profits will unlock over. * @param _releaseDelta The number of releases prior to the latest to use as a target. NOTE: Set to 0 for latest. * @return _vault address of the newly-deployed vault */ function newEndorsedVault( address _asset, string memory _name, string memory _symbol, address _roleManager, uint256 _profitMaxUnlockTime, uint256 _releaseDelta ) public virtual onlyEndorsers returns (address _vault) { // Get the target release based on the delta given. uint256 _releaseTarget = ReleaseRegistry(releaseRegistry) .numReleases() - 1 - _releaseDelta; // Get the factory address for that specific Api version. address factory = ReleaseRegistry(releaseRegistry).factories( _releaseTarget ); // Make sure we got an actual factory require(factory != address(0), "Registry: unknown release"); // Deploy New vault. _vault = IVaultFactory_1(factory).deploy_new_vault( _asset, _name, _symbol, _roleManager, _profitMaxUnlockTime ); // Register the vault with this Registry _registerVault( _vault, _asset, _releaseTarget, MULTI_STRATEGY_TYPE, block.timestamp ); } /** * @notice Endorse an already deployed multi strategy vault. * @dev To be used with default values for `_releaseDelta`, `_vaultType` * and `_deploymentTimestamp`. * * @param _vault Address of the vault to endorse. */ function endorseMultiStrategyVault(address _vault) external virtual { endorseVault(_vault, 0, MULTI_STRATEGY_TYPE, 0); } /** * @notice Endorse an already deployed Single Strategy vault. * @dev To be used with default values for `_releaseDelta`, `_vaultType` * and `_deploymentTimestamp`. * * @param _vault Address of the vault to endorse. */ function endorseSingleStrategyVault(address _vault) external virtual { endorseVault(_vault, 0, SINGLE_STRATEGY_TYPE, 0); } /** * @notice * Adds an existing vault to the list of "endorsed" vaults for that asset. * @dev * Throws if caller isn't `owner`. * Throws if no releases are registered yet. * Throws if `vault`'s api version does not match the release specified. * Emits a `NewEndorsedVault` event. * @param _vault The vault that will be endorsed by the Registry. * @param _releaseDelta Specify the number of releases prior to the latest to use as a target. * @param _vaultType Type of vault to endorse. * @param _deploymentTimestamp The timestamp of when the vault was deployed for FE use. */ function endorseVault( address _vault, uint256 _releaseDelta, uint256 _vaultType, uint256 _deploymentTimestamp ) public virtual onlyEndorsers { // Cannot endorse twice. require(vaultInfo[_vault].asset == address(0), "endorsed"); require(_vaultType != 0, "no 0 type"); require(_vaultType <= type(uint128).max, "type too high"); require(_deploymentTimestamp <= block.timestamp, "!deployment time"); // Will underflow if no releases created yet, or targeting prior to release history uint256 _releaseTarget = ReleaseRegistry(releaseRegistry) .numReleases() - 1 - _releaseDelta; // dev: no releases // Get the API version for the target specified string memory apiVersion = IVaultFactory_1( ReleaseRegistry(releaseRegistry).factories(_releaseTarget) ).apiVersion(); require( keccak256(bytes(IVault(_vault).apiVersion())) == keccak256(bytes((apiVersion))), "Wrong API Version" ); // Add to the end of the list of vaults for asset _registerVault( _vault, IVault(_vault).asset(), _releaseTarget, _vaultType, _deploymentTimestamp ); } /** * @dev Function used to register a newly deployed or added vault. * * This well set all of the values for the vault in the `vaultInfo` * mapping as well as add the vault and the underlying asset to any * relevant arrays for tracking. * */ function _registerVault( address _vault, address _asset, uint256 _releaseTarget, uint256 _vaultType, uint256 _deploymentTimestamp ) internal virtual { // Set the Info struct for this vault vaultInfo[_vault] = Info({ asset: _asset, releaseVersion: uint96(_releaseTarget), vaultType: uint64(_vaultType), deploymentTimestamp: uint128(_deploymentTimestamp), index: uint64(_endorsedVaults[_asset].length), tag: "" }); // Add to the endorsed vaults array. _endorsedVaults[_asset].push(_vault); if (!assetIsUsed[_asset]) { // We have a new asset to add assets.push(_asset); assetIsUsed[_asset] = true; } emit NewEndorsedVault(_vault, _asset, _releaseTarget, _vaultType); } /** * @notice Tag a vault with a specific string. * @dev This is available to governance to tag any vault or strategy * on chain if desired to arbitrarily classify any vaults. * i.e. Certain ratings ("A") / Vault status ("Shutdown") etc. * * @param _vault Address of the vault or strategy to tag. * @param _tag The string to tag the vault or strategy with. */ function tagVault( address _vault, string memory _tag ) external virtual onlyTaggers { require(vaultInfo[_vault].asset != address(0), "!Endorsed"); vaultInfo[_vault].tag = _tag; emit VaultTagged(_vault); } /** * @notice Remove a `_vault`. * @dev Can be used as an efficient way to remove a vault * to not have to iterate over the full array. * * NOTE: This will not remove the asset from the `assets` array * if it is no longer in use and will have to be done manually. * * @param _vault Address of the vault to remove. */ function removeVault(address _vault) external virtual onlyEndorsers { // Get the struct with all the vaults data. Info memory info = vaultInfo[_vault]; require(info.asset != address(0), "!endorsed"); require( _endorsedVaults[info.asset][info.index] == _vault, "wrong vault" ); // Get the vault at the end of the array address lastVault = _endorsedVaults[info.asset][ _endorsedVaults[info.asset].length - 1 ]; // If `_vault` is not the last item in the array. if (lastVault != _vault) { // Set the last index to the spot we are removing. _endorsedVaults[info.asset][info.index] = lastVault; // Update the index of the vault we moved vaultInfo[lastVault].index = uint64(info.index); } // Pop the last item off the array. _endorsedVaults[info.asset].pop(); // Emit the event. emit RemovedVault( _vault, info.asset, info.releaseVersion, info.vaultType ); // Delete the struct. delete vaultInfo[_vault]; } /** * @notice Removes a specific `_asset` at `_index` from `assets`. * @dev Can be used if an asset is no longer in use after a vault or * strategy has also been removed. * * @param _asset The asset to remove from the array. * @param _index The index it sits at. */ function removeAsset( address _asset, uint256 _index ) external virtual onlyEndorsers { require(assetIsUsed[_asset], "!in use"); require(_endorsedVaults[_asset].length == 0, "still in use"); require(assets[_index] == _asset, "wrong asset"); // Replace `_asset` with the last index. assets[_index] = assets[assets.length - 1]; // Pop last item off the array. assets.pop(); // No longer used. assetIsUsed[_asset] = false; } /** * @notice Set a new address to be able to endorse or remove an existing endorser. * @param _account The address to set. * @param _canEndorse Bool if the `_account` can or cannot endorse. */ function setEndorser( address _account, bool _canEndorse ) external virtual onlyGovernance { endorsers[_account] = _canEndorse; emit UpdateEndorser(_account, _canEndorse); } /** * @notice Set a new address to be able to tag a vault. * @param _account The address to set. * @param _canTag Bool if the `_account` can or cannot tag. */ function setTagger( address _account, bool _canTag ) external virtual onlyGovernance { taggers[_account] = _canTag; emit UpdateTagger(_account, _canTag); } /** * @notice Set a legacy registry if one exists. * @param _legacyRegistry The address of the legacy registry. */ function setLegacyRegistry( address _legacyRegistry ) external virtual onlyGovernance { legacyRegistry = _legacyRegistry; } } // src/debtAllocators/DebtAllocatorFactory.sol /** * @title YearnV3 Debt Allocator Factory * @author yearn.finance * @notice * Factory to deploy a debt allocator for a YearnV3 vault. */ contract DebtAllocatorFactory is Clonable { /// @notice An event emitted when a new debt allocator is added or deployed. event NewDebtAllocator( address indexed allocator, address indexed governance ); constructor() { // Deploy a dummy allocator as the original. original = address(new DebtAllocator()); } /** * @notice Clones a new debt allocator. * @param _governance Address to control the debt allocator. * @return newAllocator Address of the new debt allocator */ function newDebtAllocator( address _governance ) public virtual returns (address newAllocator) { // Clone new allocator off the original. newAllocator = _clone(); // Initialize the new allocator. DebtAllocator(newAllocator).initialize(_governance); // Emit event. emit NewDebtAllocator(newAllocator, _governance); } } // src/registry/RegistryFactory.sol /** * @title YearnV3 Registry Factory * @author yearn.finance * @notice * Factory for anyone to easily deploy their own Registry. */ contract RegistryFactory { event NewRegistry( address indexed newRegistry, address indexed governance, string name ); // The release registry to use for all Registries. address public immutable releaseRegistry; constructor(address _releaseRegistry) { releaseRegistry = _releaseRegistry; } function name() external pure virtual returns (string memory) { return "Yearn V3 Vault Registry Factory"; } /** * @notice Deploy a new Registry. * @dev Default to msg.sender for governance. * @param _name The name of the new registry. * @return Address of the new Registry. */ function createNewRegistry( string memory _name ) external virtual returns (address) { return createNewRegistry(_name, msg.sender); } /** * @notice Deploy a new Registry. * @param _name The name of the new registry. * @param _governance Address to set as governance. * @return Address of the new Registry. */ function createNewRegistry( string memory _name, address _governance ) public virtual returns (address) { Registry newRegistry = new Registry( _governance, _name, releaseRegistry ); emit NewRegistry(address(newRegistry), _governance, _name); return address(newRegistry); } } // src/accountants/Accountant.sol /// @title Accountant. /// @dev Will charge fees, issue refunds, and run health check on any reported /// gains or losses during a strategy's report. contract Accountant { using SafeERC20 for ERC20; /// @notice An event emitted when a vault is added or removed. event VaultChanged(address indexed vault, ChangeType change); /// @notice An event emitted when the default fee configuration is updated. event UpdateDefaultFeeConfig(Fee defaultFeeConfig); /// @notice An event emitted when the future fee manager is set. event SetFutureFeeManager(address indexed futureFeeManager); /// @notice An event emitted when a new fee manager is accepted. event NewFeeManager(address indexed feeManager); /// @notice An event emitted when a new vault manager is set. event UpdateVaultManager(address indexed newVaultManager); /// @notice An event emitted when the fee recipient is updated. event UpdateFeeRecipient( address indexed oldFeeRecipient, address indexed newFeeRecipient ); /// @notice An event emitted when a custom fee configuration is updated. event UpdateCustomFeeConfig(address indexed vault, Fee custom_config); /// @notice An event emitted when a custom fee configuration is removed. event RemovedCustomFeeConfig(address indexed vault); /// @notice An event emitted when the `maxLoss` parameter is updated. event UpdateMaxLoss(uint256 maxLoss); /// @notice An event emitted when rewards are distributed. event DistributeRewards(address indexed token, uint256 rewards); /// @notice Enum defining change types (added or removed). enum ChangeType { NULL, ADDED, REMOVED } /// @notice Struct representing fee details. struct Fee { uint16 managementFee; // Annual management fee to charge. uint16 performanceFee; // Performance fee to charge. uint16 refundRatio; // Refund ratio to give back on losses. uint16 maxFee; // Max fee allowed as a percent of gain. uint16 maxGain; // Max percent gain a strategy can report. uint16 maxLoss; // Max percent loss a strategy can report. bool custom; // Flag to set for custom configs. } modifier onlyFeeManager() { _checkFeeManager(); _; } modifier onlyVaultOrFeeManager() { _checkVaultOrFeeManager(); _; } modifier onlyFeeManagerOrRecipient() { _checkFeeManagerOrRecipient(); _; } modifier onlyAddedVaults() { _checkVaultIsAdded(); _; } function _checkFeeManager() internal view virtual { require(msg.sender == feeManager, "!fee manager"); } function _checkVaultOrFeeManager() internal view virtual { require( msg.sender == feeManager || msg.sender == vaultManager, "!vault manager" ); } function _checkFeeManagerOrRecipient() internal view virtual { require( msg.sender == feeRecipient || msg.sender == feeManager, "!recipient" ); } function _checkVaultIsAdded() internal view virtual { require(vaults[msg.sender], "vault not added"); } /// @notice Constant defining the maximum basis points. uint256 internal constant MAX_BPS = 10_000; /// @notice Constant defining the number of seconds in a year. uint256 internal constant SECS_PER_YEAR = 31_556_952; /// @notice Constant defining the management fee threshold. uint16 public constant MANAGEMENT_FEE_THRESHOLD = 200; /// @notice Constant defining the performance fee threshold. uint16 public constant PERFORMANCE_FEE_THRESHOLD = 5_000; /// @notice The amount of max loss to use when redeeming from vaults. uint256 public maxLoss; /// @notice The address of the fee manager. address public feeManager; /// @notice The address of the fee recipient. address public feeRecipient; /// @notice An address that can add or remove vaults. address public vaultManager; /// @notice The address of the future fee manager. address public futureFeeManager; /// @notice The default fee configuration. Fee public defaultConfig; /// @notice Mapping to track added vaults. mapping(address => bool) public vaults; /// @notice Mapping vault => custom Fee config if any. mapping(address => Fee) public customConfig; /// @notice Mapping vault => strategy => flag for one time healthcheck skips. mapping(address => mapping(address => bool)) skipHealthCheck; constructor( address _feeManager, address _feeRecipient, uint16 defaultManagement, uint16 defaultPerformance, uint16 defaultRefund, uint16 defaultMaxFee, uint16 defaultMaxGain, uint16 defaultMaxLoss ) { require(_feeManager != address(0), "ZERO ADDRESS"); require(_feeRecipient != address(0), "ZERO ADDRESS"); feeManager = _feeManager; feeRecipient = _feeRecipient; _updateDefaultConfig( defaultManagement, defaultPerformance, defaultRefund, defaultMaxFee, defaultMaxGain, defaultMaxLoss ); } /** * @notice Called by a vault when a `strategy` is reporting. * @dev The msg.sender must have been added to the `vaults` mapping. * @param strategy Address of the strategy reporting. * @param gain Amount of the gain if any. * @param loss Amount of the loss if any. * @return totalFees if any to charge. * @return totalRefunds if any for the vault to pull. */ function report( address strategy, uint256 gain, uint256 loss ) public virtual onlyAddedVaults returns (uint256 totalFees, uint256 totalRefunds) { // Declare the config to use as the custom. Fee memory fee = customConfig[msg.sender]; // Check if there is a custom config to use. if (!fee.custom) { // Otherwise use the default. fee = defaultConfig; } // Retrieve the strategy's params from the vault. IVault.StrategyParams memory strategyParams = IVault(msg.sender) .strategies(strategy); // Charge management fees no matter gain or loss. if (fee.managementFee > 0) { // Time since the last harvest. uint256 duration = block.timestamp - strategyParams.last_report; // managementFee is an annual amount, so charge based on the time passed. totalFees = ((strategyParams.current_debt * duration * (fee.managementFee)) / MAX_BPS / SECS_PER_YEAR); } // Only charge performance fees if there is a gain. if (gain > 0) { // If we are skipping the healthcheck this report if (skipHealthCheck[msg.sender][strategy]) { // Make sure it is reset for the next one. skipHealthCheck[msg.sender][strategy] = false; // Setting `maxGain` to 0 will disable the healthcheck on profits. } else if (fee.maxGain > 0) { require( gain <= (strategyParams.current_debt * (fee.maxGain)) / MAX_BPS, "too much gain" ); } totalFees += (gain * (fee.performanceFee)) / MAX_BPS; } else { // If we are skipping the healthcheck this report if (skipHealthCheck[msg.sender][strategy]) { // Make sure it is reset for the next one. skipHealthCheck[msg.sender][strategy] = false; // Setting `maxLoss` to 10_000 will disable the healthcheck on losses. } else if (fee.maxLoss < MAX_BPS) { require( loss <= (strategyParams.current_debt * (fee.maxLoss)) / MAX_BPS, "too much loss" ); } // Means we should have a loss. if (fee.refundRatio > 0) { // Cache the underlying asset the vault uses. address asset = IVault(msg.sender).asset(); // Give back either all we have or based on the refund ratio. totalRefunds = Math.min( (loss * (fee.refundRatio)) / MAX_BPS, ERC20(asset).balanceOf(address(this)) ); if (totalRefunds > 0) { // Approve the vault to pull the underlying asset. _checkAllowance(msg.sender, asset, totalRefunds); } } } // 0 Max fee means it is not enforced. if (fee.maxFee > 0) { // Ensure fee does not exceed the maxFee %. totalFees = Math.min((gain * (fee.maxFee)) / MAX_BPS, totalFees); } return (totalFees, totalRefunds); } /** * @notice Function to add a new vault for this accountant to charge fees for. * @dev This is not used to set any of the fees for the specific vault or strategy. Each fee will be set separately. * @param vault The address of a vault to allow to use this accountant. */ function addVault(address vault) external virtual onlyVaultOrFeeManager { // Ensure the vault has not already been added. require(!vaults[vault], "already added"); vaults[vault] = true; emit VaultChanged(vault, ChangeType.ADDED); } /** * @notice Function to remove a vault from this accountant's fee charging list. * @param vault The address of the vault to be removed from this accountant. */ function removeVault(address vault) external virtual onlyVaultOrFeeManager { // Ensure the vault has been previously added. require(vaults[vault], "not added"); address asset = IVault(vault).asset(); // Remove any allowances left. if (ERC20(asset).allowance(address(this), vault) != 0) { ERC20(asset).safeApprove(vault, 0); } vaults[vault] = false; emit VaultChanged(vault, ChangeType.REMOVED); } /** * @notice Function to update the default fee configuration used for * all strategies that don't have a custom config set. * @param defaultManagement Default annual management fee to charge. * @param defaultPerformance Default performance fee to charge. * @param defaultRefund Default refund ratio to give back on losses. * @param defaultMaxFee Default max fee to allow as a percent of gain. * @param defaultMaxGain Default max percent gain a strategy can report. * @param defaultMaxLoss Default max percent loss a strategy can report. */ function updateDefaultConfig( uint16 defaultManagement, uint16 defaultPerformance, uint16 defaultRefund, uint16 defaultMaxFee, uint16 defaultMaxGain, uint16 defaultMaxLoss ) external virtual onlyFeeManager { _updateDefaultConfig( defaultManagement, defaultPerformance, defaultRefund, defaultMaxFee, defaultMaxGain, defaultMaxLoss ); } /** * @dev Updates the Accountant's default fee config. * Is used during deployment and during any future updates. */ function _updateDefaultConfig( uint16 defaultManagement, uint16 defaultPerformance, uint16 defaultRefund, uint16 defaultMaxFee, uint16 defaultMaxGain, uint16 defaultMaxLoss ) internal virtual { // Check for threshold and limit conditions. require( defaultManagement <= MANAGEMENT_FEE_THRESHOLD, "management fee threshold" ); require( defaultPerformance <= PERFORMANCE_FEE_THRESHOLD, "performance fee threshold" ); require(defaultMaxLoss <= MAX_BPS, "too high"); // Update the default fee configuration. defaultConfig = Fee({ managementFee: defaultManagement, performanceFee: defaultPerformance, refundRatio: defaultRefund, maxFee: defaultMaxFee, maxGain: defaultMaxGain, maxLoss: defaultMaxLoss, custom: false }); emit UpdateDefaultFeeConfig(defaultConfig); } /** * @notice Function to set a custom fee configuration for a specific vault. * @param vault The vault the strategy is hooked up to. * @param customManagement Custom annual management fee to charge. * @param customPerformance Custom performance fee to charge. * @param customRefund Custom refund ratio to give back on losses. * @param customMaxFee Custom max fee to allow as a percent of gain. * @param customMaxGain Custom max percent gain a strategy can report. * @param customMaxLoss Custom max percent loss a strategy can report. */ function setCustomConfig( address vault, uint16 customManagement, uint16 customPerformance, uint16 customRefund, uint16 customMaxFee, uint16 customMaxGain, uint16 customMaxLoss ) external virtual onlyFeeManager { // Ensure the vault has been added. require(vaults[vault], "vault not added"); // Check for threshold and limit conditions. require( customManagement <= MANAGEMENT_FEE_THRESHOLD, "management fee threshold" ); require( customPerformance <= PERFORMANCE_FEE_THRESHOLD, "performance fee threshold" ); require(customMaxLoss <= MAX_BPS, "too high"); // Create the vault's custom config. Fee memory _config = Fee({ managementFee: customManagement, performanceFee: customPerformance, refundRatio: customRefund, maxFee: customMaxFee, maxGain: customMaxGain, maxLoss: customMaxLoss, custom: true }); // Store the config. customConfig[vault] = _config; emit UpdateCustomFeeConfig(vault, _config); } /** * @notice Function to remove a previously set custom fee configuration for a vault. * @param vault The vault to remove custom setting for. */ function removeCustomConfig(address vault) external virtual onlyFeeManager { // Ensure custom fees are set for the specified vault. require(customConfig[vault].custom, "No custom fees set"); // Set all the vaults's custom fees to 0. delete customConfig[vault]; // Emit relevant event. emit RemovedCustomFeeConfig(vault); } /** * @notice Turn off the health check for a specific `vault` `strategy` combo. * @dev This will only last for one report and get automatically turned back on. * @param vault Address of the vault. * @param strategy Address of the strategy. */ function turnOffHealthCheck( address vault, address strategy ) external virtual onlyFeeManager { // Ensure the vault has been added. require(vaults[vault], "vault not added"); skipHealthCheck[vault][strategy] = true; } /** * @notice Public getter to check for custom setting. * @dev We use uint256 for the flag since its cheaper so this * will convert it to a bool for easy view functions. * * @param vault Address of the vault. * @return If a custom fee config is set. */ function useCustomConfig( address vault ) external view virtual returns (bool) { return customConfig[vault].custom; } /** * @notice Get the full config used for a specific `vault`. * @param vault Address of the vault. * @return fee The config that would be used during the report. */ function getVaultConfig( address vault ) external view returns (Fee memory fee) { fee = customConfig[vault]; // Check if there is a custom config to use. if (!fee.custom) { // Otherwise use the default. fee = defaultConfig; } } /** * @notice Function to redeem the underlying asset from a vault. * @dev Will default to using the full balance of the vault. * @param vault The vault to redeem from. */ function redeemUnderlying(address vault) external virtual { redeemUnderlying(vault, IVault(vault).balanceOf(address(this))); } /** * @notice Function to redeem the underlying asset from a vault. * @param vault The vault to redeem from. * @param amount The amount in vault shares to redeem. */ function redeemUnderlying( address vault, uint256 amount ) public virtual onlyFeeManager { IVault(vault).redeem(amount, address(this), address(this), maxLoss); } /** * @notice Sets the `maxLoss` parameter to be used on redeems. * @param _maxLoss The amount in basis points to set as the maximum loss. */ function setMaxLoss(uint256 _maxLoss) external virtual onlyFeeManager { // Ensure that the provided `maxLoss` does not exceed 100% (in basis points). require(_maxLoss <= MAX_BPS, "higher than 100%"); maxLoss = _maxLoss; // Emit an event to signal the update of the `maxLoss` parameter. emit UpdateMaxLoss(_maxLoss); } /** * @notice Function to distribute all accumulated fees to the designated recipient. * @param token The token to distribute. */ function distribute(address token) external virtual { distribute(token, ERC20(token).balanceOf(address(this))); } /** * @notice Function to distribute accumulated fees to the designated recipient. * @param token The token to distribute. * @param amount amount of token to distribute. */ function distribute( address token, uint256 amount ) public virtual onlyFeeManagerOrRecipient { ERC20(token).safeTransfer(feeRecipient, amount); emit DistributeRewards(token, amount); } /** * @notice Function to set a future fee manager address. * @param _futureFeeManager The address to set as the future fee manager. */ function setFutureFeeManager( address _futureFeeManager ) external virtual onlyFeeManager { // Ensure the futureFeeManager is not a zero address. require(_futureFeeManager != address(0), "ZERO ADDRESS"); futureFeeManager = _futureFeeManager; emit SetFutureFeeManager(_futureFeeManager); } /** * @notice Function to accept the role change and become the new fee manager. * @dev This function allows the future fee manager to accept the role change and become the new fee manager. */ function acceptFeeManager() external virtual { // Make sure the sender is the future fee manager. require(msg.sender == futureFeeManager, "not future fee manager"); feeManager = futureFeeManager; futureFeeManager = address(0); emit NewFeeManager(msg.sender); } /** * @notice Function to set a new vault manager. * @param newVaultManager Address to add or remove vaults. */ function setVaultManager( address newVaultManager ) external virtual onlyFeeManager { vaultManager = newVaultManager; emit UpdateVaultManager(newVaultManager); } /** * @notice Function to set a new address to receive distributed rewards. * @param newFeeRecipient Address to receive distributed fees. */ function setFeeRecipient( address newFeeRecipient ) external virtual onlyFeeManager { // Ensure the newFeeRecipient is not a zero address. require(newFeeRecipient != address(0), "ZERO ADDRESS"); address oldRecipient = feeRecipient; feeRecipient = newFeeRecipient; emit UpdateFeeRecipient(oldRecipient, newFeeRecipient); } /** * @dev Internal safe function to make sure the contract you want to * interact with has enough allowance to pull the desired tokens. * * @param _contract The address of the contract that will move the token. * @param _token The ERC-20 token that will be getting spent. * @param _amount The amount of `_token` to be spent. */ function _checkAllowance( address _contract, address _token, uint256 _amount ) internal { if (ERC20(_token).allowance(address(this), _contract) < _amount) { ERC20(_token).safeApprove(_contract, 0); ERC20(_token).safeApprove(_contract, _amount); } } } // src/accountants/AccountantFactory.sol /** * @title AccountantFactory * @dev A factory contract for deploying Accountant contracts */ contract AccountantFactory { event NewAccountant(address indexed newAccountant); Accountant.Fee public defaultConfig; /** * @dev Constructor initializes the default configuration */ constructor() { defaultConfig = Accountant.Fee({ managementFee: 0, performanceFee: 1_000, refundRatio: 0, maxFee: 10_000, maxGain: 20_000, maxLoss: 1, custom: false }); } /** * @dev Deploys a new Accountant contract with default configuration * @return _newAccountant The address of the newly deployed Accountant contract */ function newAccountant() external returns (address) { return newAccountant( msg.sender, msg.sender, defaultConfig.managementFee, defaultConfig.performanceFee, defaultConfig.refundRatio, defaultConfig.maxFee, defaultConfig.maxGain, defaultConfig.maxLoss ); } /** * @dev Deploys a new Accountant contract with specified fee manager and recipient * @param feeManager The address to receive management and performance fees * @param feeRecipient The address to receive refund fees * @return _newAccountant The address of the newly deployed Accountant contract */ function newAccountant( address feeManager, address feeRecipient ) external returns (address) { return newAccountant( feeManager, feeRecipient, defaultConfig.managementFee, defaultConfig.performanceFee, defaultConfig.refundRatio, defaultConfig.maxFee, defaultConfig.maxGain, defaultConfig.maxLoss ); } /** * @dev Deploys a new Accountant contract with specified fee configurations * @param defaultManagement Default management fee * @param defaultPerformance Default performance fee * @param defaultRefund Default refund ratio * @param defaultMaxFee Default maximum fee * @param defaultMaxGain Default maximum gain * @param defaultMaxLoss Default maximum loss * @return _newAccountant The address of the newly deployed Accountant contract */ function newAccountant( uint16 defaultManagement, uint16 defaultPerformance, uint16 defaultRefund, uint16 defaultMaxFee, uint16 defaultMaxGain, uint16 defaultMaxLoss ) external returns (address) { return newAccountant( msg.sender, msg.sender, defaultManagement, defaultPerformance, defaultRefund, defaultMaxFee, defaultMaxGain, defaultMaxLoss ); } /** * @dev Deploys a new Accountant contract with specified fee configurations and addresses * @param feeManager The address to receive management and performance fees * @param feeRecipient The address to receive refund fees * @param defaultManagement Default management fee * @param defaultPerformance Default performance fee * @param defaultRefund Default refund ratio * @param defaultMaxFee Default maximum fee * @param defaultMaxGain Default maximum gain * @param defaultMaxLoss Default maximum loss * @return _newAccountant The address of the newly deployed Accountant contract */ function newAccountant( address feeManager, address feeRecipient, uint16 defaultManagement, uint16 defaultPerformance, uint16 defaultRefund, uint16 defaultMaxFee, uint16 defaultMaxGain, uint16 defaultMaxLoss ) public returns (address _newAccountant) { _newAccountant = address( new Accountant( feeManager, feeRecipient, defaultManagement, defaultPerformance, defaultRefund, defaultMaxFee, defaultMaxGain, defaultMaxLoss ) ); emit NewAccountant(_newAccountant); } } // src/managers/RoleManager.sol /// @title Yearn V3 Vault Role Manager. contract RoleManager is Positions { /// @notice Revert message for when a vault has already been deployed. error AlreadyDeployed(address _vault); /// @notice Emitted when a new vault has been deployed or added. event AddedNewVault( address indexed vault, address indexed debtAllocator, uint256 category ); /// @notice Emitted when a vaults debt allocator is updated. event UpdateDebtAllocator( address indexed vault, address indexed debtAllocator ); /// @notice Emitted when a vault is removed. event RemovedVault(address indexed vault); /// @notice Emitted when the defaultProfitMaxUnlockTime variable is updated. event UpdateDefaultProfitMaxUnlockTime( uint256 newDefaultProfitMaxUnlockTime ); /// @notice Config that holds all vault info. struct VaultConfig { address asset; uint256 category; address debtAllocator; uint256 index; } /*////////////////////////////////////////////////////////////// POSITION ID'S //////////////////////////////////////////////////////////////*/ /// @notice Position ID for "Pending Governance". bytes32 public constant PENDING_GOVERNANCE = keccak256("Pending Governance"); /// @notice Position ID for "Governance". bytes32 public constant GOVERNANCE = keccak256("Governance"); /// @notice Position ID for "Brain". bytes32 public constant MANAGEMENT = keccak256("Management"); /// @notice Position ID for "Keeper". bytes32 public constant KEEPER = keccak256("Keeper"); /// @notice Position ID for the "Registry". bytes32 public constant REGISTRY = keccak256("Registry"); /// @notice Position ID for the "Accountant". bytes32 public constant ACCOUNTANT = keccak256("Accountant"); /// @notice Position ID for the "Debt Allocator". bytes32 public constant DEBT_ALLOCATOR = keccak256("Debt Allocator"); /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ /// @notice Immutable address that the `role_manager` position // will be transferred to when a vault is removed. address public chad; /// @notice Array storing addresses of all managed vaults. address[] public vaults; // Encoded name so that it can be held as a constant. string internal projectName; /// @notice Default time until profits are fully unlocked for new vaults. uint256 public defaultProfitMaxUnlockTime; /// @notice Mapping of vault addresses to its config. mapping(address => VaultConfig) public vaultConfig; /// @notice Mapping of underlying asset, api version and category to vault. mapping(address => mapping(string => mapping(uint256 => address))) internal _assetToVault; constructor() { chad = address(1); } function initialize( string calldata _projectName, address _governance, address _management, address _keeper, address _registry, address _accountant, address _debtAllocator ) external { require(chad == address(0), "initialized"); require(_governance != address(0), "ZERO ADDRESS"); chad = _governance; projectName = _projectName; defaultProfitMaxUnlockTime = 7 days; // Governance gets all the roles. _setPositionHolder(GOVERNANCE, _governance); _setPositionRoles(GOVERNANCE, Roles.ALL); // Management reports, can update debt, queue, deposit limits and unlock time. _setPositionHolder(MANAGEMENT, _management); _setPositionRoles( MANAGEMENT, Roles.REPORTING_MANAGER | Roles.DEBT_MANAGER | Roles.QUEUE_MANAGER | Roles.DEPOSIT_LIMIT_MANAGER | Roles.DEBT_PURCHASER | Roles.PROFIT_UNLOCK_MANAGER ); // The keeper can process reports. _setPositionHolder(KEEPER, _keeper); _setPositionRoles(KEEPER, Roles.REPORTING_MANAGER); // Debt allocators manage debt and also need to process reports. _setPositionHolder(DEBT_ALLOCATOR, _debtAllocator); _setPositionRoles( DEBT_ALLOCATOR, Roles.REPORTING_MANAGER | Roles.DEBT_MANAGER ); _setPositionHolder(REGISTRY, _registry); _setPositionHolder(ACCOUNTANT, _accountant); } /*////////////////////////////////////////////////////////////// VAULT CREATION //////////////////////////////////////////////////////////////*/ /** * @notice Creates a new endorsed vault with default profit max unlock time. * @param _asset Address of the underlying asset. * @param _category Category of the vault. * @param _name Name of the vault. * @param _symbol Symbol of the vault. * @return _vault Address of the newly created vault. */ function newVault( address _asset, uint256 _category, string calldata _name, string calldata _symbol ) external virtual onlyPositionHolder(GOVERNANCE) returns (address) { return _newVault(_asset, _category, _name, _symbol, type(uint256).max); } /** * @notice Creates a new endorsed vault with default profit max unlock time. * @param _asset Address of the underlying asset. * @param _category Category of the vault. * @param _name Name of the vault. * @param _symbol Symbol of the vault. * @param _depositLimit The deposit limit to start the vault with. * @return _vault Address of the newly created vault. */ function newVault( address _asset, uint256 _category, string calldata _name, string calldata _symbol, uint256 _depositLimit ) external virtual onlyPositionHolder(GOVERNANCE) returns (address) { return _newVault(_asset, _category, _name, _symbol, _depositLimit); } /** * @notice Creates a new endorsed vault. * @param _asset Address of the underlying asset. * @param _category Category of the vault. * @param _name Name of the vault. * @param _symbol Symbol of the vault. * @param _depositLimit The deposit limit to start the vault with. * @return _vault Address of the newly created vault. */ function _newVault( address _asset, uint256 _category, string memory _name, string memory _symbol, uint256 _depositLimit ) internal virtual returns (address _vault) { // Deploy through the registry so it is automatically endorsed. _vault = Registry(getPositionHolder(REGISTRY)).newEndorsedVault( _asset, _name, _symbol, address(this), defaultProfitMaxUnlockTime ); // Check that a vault does not exist for that asset, api and category. // This reverts late to not waste gas when used correctly. string memory _apiVersion = IVault(_vault).apiVersion(); if (_assetToVault[_asset][_apiVersion][_category] != address(0)) { revert AlreadyDeployed( _assetToVault[_asset][_apiVersion][_category] ); } address _debtAllocator = getPositionHolder(DEBT_ALLOCATOR); // Give out roles on the new vault. _sanctify(_vault, _debtAllocator); // Set up the accountant. _setAccountant(_vault); if (_depositLimit != 0) { _setDepositLimit(_vault, _depositLimit); } // Add the vault config to the mapping. vaultConfig[_vault] = VaultConfig({ asset: _asset, category: _category, debtAllocator: _debtAllocator, index: vaults.length }); // Add the vault to the mapping. _assetToVault[_asset][_apiVersion][_category] = _vault; // Add the vault to the array. vaults.push(_vault); // Emit event for new vault. emit AddedNewVault(_vault, _debtAllocator, _category); } /** * @dev Assigns roles to the newly added vault. * * This will override any previously set roles for the holders. But not effect * the roles held by other addresses. * * @param _vault Address of the vault to sanctify. * @param _debtAllocator Address of the debt allocator for the vault. */ function _sanctify( address _vault, address _debtAllocator ) internal virtual { // Set the roles for daddy. _setRole(_vault, _positions[GOVERNANCE]); // Set the roles for Management. _setRole(_vault, _positions[MANAGEMENT]); // Set the roles for the Keeper. _setRole(_vault, _positions[KEEPER]); // Give the specific debt allocator its roles. _setRole( _vault, Position(_debtAllocator, _positions[DEBT_ALLOCATOR].roles) ); } /** * @dev Used internally to set the roles on a vault for a given position. * Will not set the roles if the position holder is address(0). * This does not check that the roles are !=0 because it is expected that * the holder will be set to 0 if the position is not being used. * * @param _vault Address of the vault. * @param _position Holder address and roles to set. */ function _setRole( address _vault, Position memory _position ) internal virtual { if (_position.holder != address(0)) { IVault(_vault).set_role(_position.holder, uint256(_position.roles)); } } /** * @dev Sets the accountant on the vault and adds the vault to the accountant. * This temporarily gives the `ACCOUNTANT_MANAGER` role to this contract. * @param _vault Address of the vault to set up the accountant for. */ function _setAccountant(address _vault) internal virtual { // Get the current accountant. address accountant = getPositionHolder(ACCOUNTANT); // If there is an accountant set. if (accountant != address(0)) { // Temporarily give this contract the ability to set the accountant. IVault(_vault).add_role(address(this), Roles.ACCOUNTANT_MANAGER); // Set the account on the vault. IVault(_vault).set_accountant(accountant); // Take away the role. IVault(_vault).remove_role(address(this), Roles.ACCOUNTANT_MANAGER); // Whitelist the vault in the accountant. Accountant(accountant).addVault(_vault); } } /** * @dev Used to set an initial deposit limit when a new vault is deployed. * Any further updates to the limit will need to be done by an address that * holds the `DEPOSIT_LIMIT_MANAGER` role. * @param _vault Address of the newly deployed vault. * @param _depositLimit The deposit limit to set. */ function _setDepositLimit( address _vault, uint256 _depositLimit ) internal virtual { // Temporarily give this contract the ability to set the deposit limit. IVault(_vault).add_role(address(this), Roles.DEPOSIT_LIMIT_MANAGER); // Set the initial deposit limit on the vault. IVault(_vault).set_deposit_limit(_depositLimit); // Take away the role. IVault(_vault).remove_role(address(this), Roles.DEPOSIT_LIMIT_MANAGER); } /*////////////////////////////////////////////////////////////// VAULT MANAGEMENT //////////////////////////////////////////////////////////////*/ /** * @notice Adds a new vault to the RoleManager with the specified category. * @dev If not already endorsed this function will endorse the vault. * A new debt allocator will be deployed and configured. * @param _vault Address of the vault to be added. * @param _category Category associated with the vault. */ function addNewVault(address _vault, uint256 _category) external virtual { addNewVault(_vault, _category, getPositionHolder(DEBT_ALLOCATOR)); } /** * @notice Adds a new vault to the RoleManager with the specified category and debt allocator. * @dev If not already endorsed this function will endorse the vault. * @param _vault Address of the vault to be added. * @param _category Category associated with the vault. * @param _debtAllocator Address of the debt allocator for the vault. */ function addNewVault( address _vault, uint256 _category, address _debtAllocator ) public virtual onlyPositionHolder(GOVERNANCE) { // Check that a vault does not exist for that asset, api and category. address _asset = IVault(_vault).asset(); string memory _apiVersion = IVault(_vault).apiVersion(); if (_assetToVault[_asset][_apiVersion][_category] != address(0)) { revert AlreadyDeployed( _assetToVault[_asset][_apiVersion][_category] ); } // If not the current role manager. if (IVault(_vault).role_manager() != address(this)) { // Accept the position of role manager. IVault(_vault).accept_role_manager(); } // Get the current registry. address registry = getPositionHolder(REGISTRY); // Check if the vault has been endorsed yet in the registry. if (!Registry(registry).isEndorsed(_vault)) { // If not endorse it. // NOTE: This will revert if adding a vault of an older version. Registry(registry).endorseMultiStrategyVault(_vault); } // Set the roles up. _sanctify(_vault, _debtAllocator); // Only set an accountant if there is not one set yet. if (IVault(_vault).accountant() == address(0)) { _setAccountant(_vault); } // Add the vault config to the mapping. vaultConfig[_vault] = VaultConfig({ asset: _asset, category: _category, debtAllocator: _debtAllocator, index: vaults.length }); // Add the vault to the mapping. _assetToVault[_asset][_apiVersion][_category] = _vault; // Add the vault to the array. vaults.push(_vault); // Emit event. emit AddedNewVault(_vault, _debtAllocator, _category); } /** * @notice Update a `_vault`s debt allocator. * @dev This will use the default Debt Allocator currently set. * @param _vault Address of the vault to update the allocator for. */ function updateDebtAllocator( address _vault ) external virtual returns (address _newDebtAllocator) { _newDebtAllocator = getPositionHolder(DEBT_ALLOCATOR); updateDebtAllocator(_vault, _newDebtAllocator); } /** * @notice Update a `_vault`s debt allocator to a specified `_debtAllocator`. * @param _vault Address of the vault to update the allocator for. * @param _debtAllocator Address of the new debt allocator. */ function updateDebtAllocator( address _vault, address _debtAllocator ) public virtual onlyPositionHolder(MANAGEMENT) { // Make sure the vault has been added to the role manager. require(vaultConfig[_vault].asset != address(0), "vault not added"); // Remove the roles from the old allocator. _setRole(_vault, Position(vaultConfig[_vault].debtAllocator, 0)); // Give the new debt allocator the relevant roles. _setRole( _vault, Position(_debtAllocator, _positions[DEBT_ALLOCATOR].roles) ); // Update the vaults config. vaultConfig[_vault].debtAllocator = _debtAllocator; // Emit event. emit UpdateDebtAllocator(_vault, _debtAllocator); } /** * @notice Update a `_vault`s keeper to a specified `_keeper`. * @param _vault Address of the vault to update the keeper for. * @param _keeper Address of the new keeper. */ function updateKeeper( address _vault, address _keeper ) external virtual onlyPositionHolder(MANAGEMENT) { // Make sure the vault has been added to the role manager. require(vaultConfig[_vault].asset != address(0), "vault not added"); // Remove the roles from the old keeper if active. address defaultKeeper = getPositionHolder(KEEPER); if ( _keeper != defaultKeeper && IVault(_vault).roles(defaultKeeper) != 0 ) { _setRole(_vault, Position(defaultKeeper, 0)); } // Give the new keeper the relevant roles. _setRole(_vault, Position(_keeper, _positions[KEEPER].roles)); } function updateVaultName( address _vault, string calldata _name ) external onlyPositionHolder(GOVERNANCE) { require(vaultConfig[_vault].asset != address(0), "vault not added"); IVault(_vault).setName(_name); } function updateVaultSymbol( address _vault, string calldata _symbol ) external onlyPositionHolder(GOVERNANCE) { require(vaultConfig[_vault].asset != address(0), "vault not added"); IVault(_vault).setSymbol(_symbol); } /** * @notice Removes a vault from the RoleManager. * @dev This will NOT un-endorse the vault from the registry. * @param _vault Address of the vault to be removed. */ function removeVault( address _vault ) external virtual onlyPositionHolder(MANAGEMENT) { // Get the vault specific config. VaultConfig memory config = vaultConfig[_vault]; // Make sure the vault has been added to the role manager. require(config.asset != address(0), "vault not added"); // Transfer the role manager position. IVault(_vault).transfer_role_manager(chad); // Address of the vault to replace it with. address vaultToMove = vaults[vaults.length - 1]; // Move the last vault to the index of `_vault` vaults[config.index] = vaultToMove; vaultConfig[vaultToMove].index = config.index; // Remove the last item. vaults.pop(); // Delete the vault from the mapping. delete _assetToVault[config.asset][IVault(_vault).apiVersion()][ config.category ]; // Delete the config for `_vault`. delete vaultConfig[_vault]; emit RemovedVault(_vault); } /** * @notice Removes a specific role(s) for a `_holder` from the `_vaults`. * @dev Can be used to remove one specific role or multiple. * @param _vaults Array of vaults to adjust. * @param _holder Address who's having a role removed. * @param _role The role or roles to remove from the `_holder`. */ function removeRoles( address[] calldata _vaults, address _holder, uint256 _role ) external virtual onlyPositionHolder(GOVERNANCE) { address _vault; for (uint256 i = 0; i < _vaults.length; ++i) { _vault = _vaults[i]; // Make sure the vault is added to this Role Manager. require(vaultConfig[_vault].asset != address(0), "vault not added"); // Remove the role. IVault(_vault).remove_role(_holder, _role); } } /*////////////////////////////////////////////////////////////// SETTERS //////////////////////////////////////////////////////////////*/ /** * @notice Setter function for updating a positions roles. * @param _position Identifier for the position. * @param _newRoles New roles for the position. */ function setPositionRoles( bytes32 _position, uint256 _newRoles ) external virtual onlyPositionHolder(GOVERNANCE) { // Cannot change the debt allocator or keeper roles since holder can be updated. require( _position != DEBT_ALLOCATOR && _position != KEEPER, "cannot update" ); _setPositionRoles(_position, _newRoles); } /** * @notice Setter function for updating a positions holder. * @dev Updating `Governance` requires setting `PENDING_GOVERNANCE` * and then the pending address calling {acceptGovernance}. * @param _position Identifier for the position. * @param _newHolder New address for position. */ function setPositionHolder( bytes32 _position, address _newHolder ) external virtual onlyPositionHolder(GOVERNANCE) { require(_position != GOVERNANCE, "!two step flow"); _setPositionHolder(_position, _newHolder); } /** * @notice Sets the default time until profits are fully unlocked for new vaults. * @param _newDefaultProfitMaxUnlockTime New value for defaultProfitMaxUnlockTime. */ function setDefaultProfitMaxUnlockTime( uint256 _newDefaultProfitMaxUnlockTime ) external virtual onlyPositionHolder(GOVERNANCE) { defaultProfitMaxUnlockTime = _newDefaultProfitMaxUnlockTime; emit UpdateDefaultProfitMaxUnlockTime(_newDefaultProfitMaxUnlockTime); } /** * @notice Accept the Governance role. * @dev Caller must be the Pending Governance. */ function acceptGovernance() external virtual onlyPositionHolder(PENDING_GOVERNANCE) { // Set the Governance role to the caller. _setPositionHolder(GOVERNANCE, msg.sender); // Reset the Pending Governance. _setPositionHolder(PENDING_GOVERNANCE, address(0)); } /*////////////////////////////////////////////////////////////// VIEW METHODS //////////////////////////////////////////////////////////////*/ /** * @notice Get the name of this contract. */ function name() external view virtual returns (string memory) { return string(abi.encodePacked(projectName, " Role Manager")); } /** * @notice Get all vaults that this role manager controls.. * @return The full array of vault addresses. */ function getAllVaults() external view virtual returns (address[] memory) { return vaults; } /** * @notice Get the vault for a specific asset, api and category. * @dev This will return address(0) if one has not been added or deployed. * * @param _asset The underlying asset used. * @param _apiVersion The version of the vault. * @param _category The category of the vault. * @return The vault for the specified `_asset`, `_apiVersion` and `_category`. */ function getVault( address _asset, string memory _apiVersion, uint256 _category ) external view virtual returns (address) { return _assetToVault[_asset][_apiVersion][_category]; } /** * @notice Get the latest vault for a specific asset. * @dev This will default to using category 1. * @param _asset The underlying asset used. * @return _vault latest vault for the specified `_asset` if any. */ function latestVault( address _asset ) external view virtual returns (address) { return latestVault(_asset, 1); } /** * @notice Get the latest vault for a specific asset. * @param _asset The underlying asset used. * @param _category The category of the vault. * @return _vault latest vault for the specified `_asset` if any. */ function latestVault( address _asset, uint256 _category ) public view virtual returns (address _vault) { address releaseRegistry = Registry(getPositionHolder(REGISTRY)) .releaseRegistry(); uint256 numReleases = ReleaseRegistry(releaseRegistry).numReleases(); for (uint256 i = numReleases; i > 0; --i) { string memory apiVersion = IVaultFactory_0( ReleaseRegistry(releaseRegistry).factories(i - 1) ).apiVersion(); _vault = _assetToVault[_asset][apiVersion][_category]; if (_vault != address(0)) { break; } } } /** * @notice Check if a vault is managed by this contract. * @dev This will check if the `asset` variable in the struct has been * set for an easy external view check. * * Does not check the vaults `role_manager` position since that can be set * by anyone for a random vault. * * @param _vault Address of the vault to check. * @return . The vaults role manager status. */ function isVaultsRoleManager( address _vault ) external view virtual returns (bool) { return vaultConfig[_vault].asset != address(0); } /** * @notice Get the debt allocator for a specific vault. * @dev Will return address(0) if the vault is not managed by this contract. * @param _vault Address of the vault. * @return . Address of the debt allocator if any. */ function getDebtAllocator( address _vault ) external view virtual returns (address) { return vaultConfig[_vault].debtAllocator; } /** * @notice Get the category for a specific vault. * @dev Will return 0 if the vault is not managed by this contract. * @param _vault Address of the vault. * @return . The category of the vault if any. */ function getCategory( address _vault ) external view virtual returns (uint256) { return vaultConfig[_vault].category; } /** * @notice Get the address assigned to the Governance position. * @return The address assigned to the Governance position. */ function getGovernance() external view virtual returns (address) { return getPositionHolder(GOVERNANCE); } /** * @notice Get the address assigned to the Pending Governance position. * @return The address assigned to the Pending Governance position. */ function getPendingGovernance() external view virtual returns (address) { return getPositionHolder(PENDING_GOVERNANCE); } /** * @notice Get the address assigned to the Management position. * @return The address assigned to the Management position. */ function getManagement() external view virtual returns (address) { return getPositionHolder(MANAGEMENT); } /** * @notice Get the address assigned to the Keeper position. * @return The address assigned to the Keeper position. */ function getKeeper() external view virtual returns (address) { return getPositionHolder(KEEPER); } /** * @notice Get the address assigned to the Registry. * @return The address assigned to the Registry. */ function getRegistry() external view virtual returns (address) { return getPositionHolder(REGISTRY); } /** * @notice Get the address assigned to the accountant. * @return The address assigned to the accountant. */ function getAccountant() external view virtual returns (address) { return getPositionHolder(ACCOUNTANT); } /** * @notice Get the address assigned to be the debt allocator if any. * @return The address assigned to be the debt allocator if any. */ function getDebtAllocator() external view virtual returns (address) { return getPositionHolder(DEBT_ALLOCATOR); } /** * @notice Get the roles given to the Governance position. * @return The roles given to the Governance position. */ function getGovernanceRoles() external view virtual returns (uint256) { return getPositionRoles(GOVERNANCE); } /** * @notice Get the roles given to the Management position. * @return The roles given to the Management position. */ function getManagementRoles() external view virtual returns (uint256) { return getPositionRoles(MANAGEMENT); } /** * @notice Get the roles given to the Keeper position. * @return The roles given to the Keeper position. */ function getKeeperRoles() external view virtual returns (uint256) { return getPositionRoles(KEEPER); } /** * @notice Get the roles given to the debt allocators. * @return The roles given to the debt allocators. */ function getDebtAllocatorRoles() external view virtual returns (uint256) { return getPositionRoles(DEBT_ALLOCATOR); } } // src/managers/RoleManagerFactory.sol /// @title Role Manager Factory /// @dev Used to either deploy single generic Role Managers or /// to easily configure and setup a new project that uses the Yearn V3 system. contract RoleManagerFactory is Clonable { event NewRoleManager(address indexed roleManager); event NewProject(bytes32 indexed projectId, address indexed roleManager); struct Project { address roleManager; address registry; address accountant; address debtAllocator; } /// @notice Position ID for the Accountant. bytes32 public constant ACCOUNTANT_FACTORY = keccak256("Accountant Factory"); /// @notice Position ID for Debt Allocator Factory bytes32 public constant DEBT_ALLOCATOR_FACTORY = keccak256("Debt Allocator Factory"); bytes32 public constant KEEPER = keccak256("Keeper"); /// @notice Position ID for the Registry. bytes32 public constant REGISTRY_FACTORY = keccak256("Registry Factory"); string public apiVersion = "3.0.3"; address public immutable protocolAddressProvider; mapping(bytes32 => Project) public projects; constructor(address _addressProvider) { protocolAddressProvider = _addressProvider; original = address(new RoleManager()); } /** * @notice Create a new RoleManager instance * @param _projectName The name of the project * @param _governance The address of governance * @param _management The address of management * @param _keeper The address of the keeper * @param _registry The address of the projects registry * @param _accountant The address of the projects accountant * @param _debtAllocator The address of the projects debt allocator * @return _roleManager address of the newly created RoleManager */ function newRoleManager( string calldata _projectName, address _governance, address _management, address _keeper, address _registry, address _accountant, address _debtAllocator ) external virtual returns (address _roleManager) { _roleManager = _clone(); RoleManager(_roleManager).initialize( _projectName, _governance, _management, _keeper, _registry, _accountant, _debtAllocator ); emit NewRoleManager(_roleManager); } /** * @notice Create a new project with associated periphery contracts. * This will deploy and complete full setup with default configuration for * a new V3 project to exist. * @param _name The name of the project * @param _governance The address of governance to use * @param _management The address of management to use if any * @return _roleManager address of the newly created RoleManager for the project */ function newProject( string calldata _name, address _governance, address _management ) external virtual returns (address _roleManager) { bytes32 _id = getProjectId(_name, _governance); require(projects[_id].roleManager == address(0), "project exists"); // Deploy the needed periphery contracts. address _registry = RegistryFactory( _fromAddressProvider(REGISTRY_FACTORY) ).createNewRegistry(string(abi.encodePacked(_name, " Registry"))); address _accountant = AccountantFactory( _fromAddressProvider(ACCOUNTANT_FACTORY) ).newAccountant(address(this), _governance); // If management is not used, use governance as the default owner of the debt allocator. address _debtAllocator = DebtAllocatorFactory( _fromAddressProvider(DEBT_ALLOCATOR_FACTORY) ).newDebtAllocator( _management != address(0) ? _management : _governance ); // Clone and initialize the RoleManager. _roleManager = _clone(); RoleManager(_roleManager).initialize( _name, _governance, _management, _fromAddressProvider(KEEPER), _registry, _accountant, _debtAllocator ); // Give Role Manager the needed access in the registry and accountant. Registry(_registry).setEndorser(_roleManager, true); Registry(_registry).transferGovernance(_governance); Accountant(_accountant).setVaultManager(_roleManager); Accountant(_accountant).setFutureFeeManager(_governance); projects[_id] = Project({ roleManager: _roleManager, registry: _registry, accountant: _accountant, debtAllocator: _debtAllocator }); emit NewProject(_id, _roleManager); } /** * @notice Generates a unique project ID * @param _name The name of the project * @param _governance The address of the governance * @return The generated project ID */ function getProjectId( string memory _name, address _governance ) public view virtual returns (bytes32) { return keccak256(abi.encodePacked(_name, _governance, block.chainid)); } /** * @notice Retrieves an address from the protocol address provider * @param _id The ID of the address to retrieve * @return The retrieved address */ function _fromAddressProvider(bytes32 _id) internal view returns (address) { return IProtocolAddressProvider(protocolAddressProvider).getAddress(_id); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_addressProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"projectId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"roleManager","type":"address"}],"name":"NewProject","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"roleManager","type":"address"}],"name":"NewRoleManager","type":"event"},{"inputs":[],"name":"ACCOUNTANT_FACTORY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEBT_ALLOCATOR_FACTORY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRY_FACTORY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"apiVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_governance","type":"address"}],"name":"getProjectId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_management","type":"address"}],"name":"newProject","outputs":[{"internalType":"address","name":"_roleManager","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_projectName","type":"string"},{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_registry","type":"address"},{"internalType":"address","name":"_accountant","type":"address"},{"internalType":"address","name":"_debtAllocator","type":"address"}],"name":"newRoleManager","outputs":[{"internalType":"address","name":"_roleManager","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"original","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"projects","outputs":[{"internalType":"address","name":"roleManager","type":"address"},{"internalType":"address","name":"registry","type":"address"},{"internalType":"address","name":"accountant","type":"address"},{"internalType":"address","name":"debtAllocator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolAddressProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60e0604052600560a090815264332e302e3360d81b60c0526001906200002690826200016a565b503480156200003457600080fd5b506040516200435238038062004352833981016040819052620000579162000236565b6001600160a01b0381166080526040516200007290620000b7565b604051809103906000f0801580156200008f573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b03929092169190911790555062000268565b613216806200113c83390190565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620000f057607f821691505b6020821081036200011157634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200016557600081815260208120601f850160051c81016020861015620001405750805b601f850160051c820191505b8181101562000161578281556001016200014c565b5050505b505050565b81516001600160401b03811115620001865762000186620000c5565b6200019e81620001978454620000db565b8462000117565b602080601f831160018114620001d65760008415620001bd5750858301515b600019600386901b1c1916600185901b17855562000161565b600085815260208120601f198616915b828110156200020757888601518255948401946001909101908401620001e6565b5085821015620002265787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156200024957600080fd5b81516001600160a01b03811681146200026157600080fd5b9392505050565b608051610eb16200028b6000396000818161014001526109bb0152610eb16000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063511abc9011610071578063511abc9014610162578063861ceb6914610175578063862a179e1461019c578063b96ea12d146101c3578063c97bbd3f1461023c578063ec6d26211461024f57600080fd5b80631fe833b1146100ae57806325829410146100e857806346c715fa146100fd57806347cbf63e146101285780634c73e7791461013b575b600080fd5b6100d57f26555a2400e5f7a7dc5f4794e1040ade6c5cec3c38b80ceaff00502a62e4b65881565b6040519081526020015b60405180910390f35b6100f0610276565b6040516100df9190610aaa565b600054610110906001600160a01b031681565b6040516001600160a01b0390911681526020016100df565b610110610136366004610b4e565b610304565b6101107f000000000000000000000000000000000000000000000000000000000000000081565b6100d5610170366004610c18565b6103bd565b6100d57f8975f7470d8885db19a03ab19387b0d032119bec5e894c777643a8eedb564af481565b6100d57f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de81565b6102096101d1366004610cda565b600260208190526000918252604090912080546001820154928201546003909201546001600160a01b03918216938216928216911684565b604080516001600160a01b03958616815293851660208501529184169183019190915290911660608201526080016100df565b61011061024a366004610cf3565b6103f2565b6100d57f15cf1584ec2aa3ee42f6f37421830cb25dd95e2e2b575c279686a2b05aeeaa3a81565b6001805461028390610d5b565b80601f01602080910402602001604051908101604052809291908181526020018280546102af90610d5b565b80156102fc5780601f106102d1576101008083540402835291602001916102fc565b820191906000526020600020905b8154815290600101906020018083116102df57829003601f168201915b505050505081565b600061030e610987565b60405163f796e58760e01b81529091506001600160a01b0382169063f796e5879061034b908c908c908c908c908c908c908c908c90600401610d95565b600060405180830381600087803b15801561036557600080fd5b505af1158015610379573d6000803e3d6000fd5b50506040516001600160a01b03841692507f078e7fe644f074748ec021662e935d12aa303bd3e32403f200c621b7cd8c26109150600090a298975050505050505050565b60008282466040516020016103d493929190610dfa565b60405160208183030381529060405280519060200120905092915050565b60008061043686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506103bd915050565b6000818152600260205260409020549091506001600160a01b0316156104935760405162461bcd60e51b815260206004820152600e60248201526d70726f6a6563742065786973747360901b604482015260640160405180910390fd5b60006104be7f8975f7470d8885db19a03ab19387b0d032119bec5e894c777643a8eedb564af46109a2565b6001600160a01b031663e2eb36ee88886040516020016104df929190610e39565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161050a9190610aaa565b6020604051808303816000875af1158015610529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054d9190610e57565b9050600061057a7f15cf1584ec2aa3ee42f6f37421830cb25dd95e2e2b575c279686a2b05aeeaa3a6109a2565b60405163f37bb0ff60e01b81523060048201526001600160a01b038881166024830152919091169063f37bb0ff906044016020604051808303816000875af11580156105ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ee9190610e57565b9050600061061b7f26555a2400e5f7a7dc5f4794e1040ade6c5cec3c38b80ceaff00502a62e4b6586109a2565b6001600160a01b03908116906328d34c90908816610639578861063b565b875b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303816000875af1158015610681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a59190610e57565b90506106af610987565b9450846001600160a01b031663f796e5878a8a8a8a6106ed7f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de6109a2565b8989896040518963ffffffff1660e01b8152600401610713989796959493929190610d95565b600060405180830381600087803b15801561072d57600080fd5b505af1158015610741573d6000803e3d6000fd5b5050604051632c2a72d560e01b81526001600160a01b0388811660048301526001602483015286169250632c2a72d59150604401600060405180830381600087803b15801561078f57600080fd5b505af11580156107a3573d6000803e3d6000fd5b50506040516334e2fffd60e21b81526001600160a01b038a811660048301528616925063d38bfff49150602401600060405180830381600087803b1580156107ea57600080fd5b505af11580156107fe573d6000803e3d6000fd5b5050604051635aa1a81f60e11b81526001600160a01b0388811660048301528516925063b543503e9150602401600060405180830381600087803b15801561084557600080fd5b505af1158015610859573d6000803e3d6000fd5b5050604051639b3b695560e01b81526001600160a01b038a8116600483015285169250639b3b69559150602401600060405180830381600087803b1580156108a057600080fd5b505af11580156108b4573d6000803e3d6000fd5b5050604080516080810182526001600160a01b03808a1680835288821660208085019182528984168587019081528985166060870190815260008e8152600293849052888120975188549088166001600160a01b03199182161789559451600189018054918916918716919091179055915192870180549387169385169390931790925590516003909501805495909416949091169390931790915591519193508792507f8a7d5af3ee2f0fbd1d46dea22a1f5283b0f0716d637b79e7f721f87e0f94c1eb91a350505050949350505050565b6000805461099d906001600160a01b0316610a34565b905090565b6040516321f8a72160e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906321f8a72190602401602060405180830381865afa158015610a0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2e9190610e57565b92915050565b6000808260601b9050604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528160148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f0949350505050565b60005b83811015610aa1578181015183820152602001610a89565b50506000910152565b6020815260008251806020840152610ac9816040850160208701610a86565b601f01601f19169190910160400192915050565b60008083601f840112610aef57600080fd5b50813567ffffffffffffffff811115610b0757600080fd5b602083019150836020828501011115610b1f57600080fd5b9250929050565b6001600160a01b0381168114610b3b57600080fd5b50565b8035610b4981610b26565b919050565b60008060008060008060008060e0898b031215610b6a57600080fd5b883567ffffffffffffffff811115610b8157600080fd5b610b8d8b828c01610add565b9099509750506020890135610ba181610b26565b95506040890135610bb181610b26565b94506060890135610bc181610b26565b93506080890135610bd181610b26565b925060a0890135610be181610b26565b915060c0890135610bf181610b26565b809150509295985092959890939650565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610c2b57600080fd5b823567ffffffffffffffff80821115610c4357600080fd5b818501915085601f830112610c5757600080fd5b813581811115610c6957610c69610c02565b604051601f8201601f19908116603f01168101908382118183101715610c9157610c91610c02565b81604052828152886020848701011115610caa57600080fd5b826020860160208301376000602084830101528096505050505050610cd160208401610b3e565b90509250929050565b600060208284031215610cec57600080fd5b5035919050565b60008060008060608587031215610d0957600080fd5b843567ffffffffffffffff811115610d2057600080fd5b610d2c87828801610add565b9095509350506020850135610d4081610b26565b91506040850135610d5081610b26565b939692955090935050565b600181811c90821680610d6f57607f821691505b602082108103610d8f57634e487b7160e01b600052602260045260246000fd5b50919050565b60e081528760e08201526000610100898b828501376000838b018201526001600160a01b03988916602084015296881660408301525093861660608501529185166080840152841660a083015290921660c0830152601f909201601f19160101919050565b60008451610e0c818460208901610a86565b60609490941b6bffffffffffffffffffffffff191691909301908152601481019190915260340192915050565b818382376820526567697374727960b81b9101908152600901919050565b600060208284031215610e6957600080fd5b8151610e7481610b26565b939250505056fea26469706673582212205090e5a7e9a2ede6d6f8f3f05124374882a5864fd9c74a25bfcb786b1ec4a9f464736f6c63430008120033608060405234801561001057600080fd5b50600180546001600160a01b031916811790556131e4806100326000396000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063862a179e11610182578063ba628b27116100e9578063e0a4edf3116100a2578063ef8d03da1161007c578063ef8d03da1461068d578063f796e587146106a0578063f857ef67146106b3578063fecad85a1461072b57600080fd5b8063e0a4edf314610654578063e177dc7014610667578063e7c512771461067a57600080fd5b8063ba628b27146105ed578063bfb7d4a314610600578063caa61cb014610613578063ceb68c2314610626578063d013355a14610639578063deb20d871461064157600080fd5b806397331bf91161013b57806397331bf9146105825780639cf0e60714610597578063a1b77046146105aa578063b4936a59146105b2578063b53f0d00146105c5578063b8efd717146105da57600080fd5b8063862a179e146105205780638808f66d146105355780638b9d29401461054a5780638c64ea4a1461055f5780638e8a3a4c1461057257806390ca03d01461057a57600080fd5b806338906554116102265780635ab1bd53116101df5780635ab1bd53146104645780635dcd67451461046c57806364724604146104aa5780637a0c2b70146104d95780637de9cd22146104ec5780638440d167146104f457600080fd5b80633890655414610411578063391b6f4e146104245780633b1100fa1461042c5780633bc3f9ea146104415780633dcd4f2e146104495780633f6054681461045157600080fd5b806314f675d21161027857806314f675d2146103585780631507a04d1461036d5780631928b3cb14610380578063238efcbc146103ee578063289b3c0d146103f65780633080cef1146103fe57600080fd5b806306433b1b146102c057806306fdde03146102e85780630a1c187c146102fd5780630b63a38a14610328578063146278341461033b57806314adb2f314610350575b600080fd5b6102d56000805160206130cf83398151915281565b6040519081526020015b60405180910390f35b6102f0610734565b6040516102df9190612746565b61031061030b366004612760565b61075c565b6040516001600160a01b0390911681526020016102df565b6103106103363660046127d7565b610777565b6102d560008051602061318f83398151915281565b6102d5610815565b61036b61036636600461286c565b610833565b005b61036b61037b3660046128a5565b610994565b6103cf61038e366004612760565b600090815260208181526040918290208251808401909352546001600160a01b038116808452600160a01b9091046001600160601b03169290910182905291565b604080516001600160a01b0390931683526020830191909152016102df565b61036b610a95565b610310610ae1565b61036b61040c366004612931565b610afa565b61036b61041f36600461286c565b611029565b6103106111c9565b6102d560008051602061314f83398151915281565b6103106111e2565b6102d56111fb565b61036b61045f366004612760565b611214565b610310611268565b61049a61047a366004612973565b6001600160a01b0390811660009081526005602052604090205416151590565b60405190151581526020016102df565b6103106104b8366004612973565b6001600160a01b039081166000908152600560205260409020600201541690565b61036b6104e7366004612990565b611281565b610310611336565b6102d5610502366004612973565b6001600160a01b031660009081526005602052604090206001015490565b6102d560008051602061312f83398151915281565b6102d56000805160206130ef83398151915281565b6102d560008051602061310f83398151915281565b61031061056d366004612760565b61134f565b6102d5611379565b610310611392565b61058a6113ab565b6040516102df91906129e5565b6103106105a5366004612a32565b61140d565b61031061164f565b61036b6105c0366004612a5e565b611668565b6102d560008051602061316f83398151915281565b61036b6105e8366004612990565b6116dd565b6103106105fb366004612a83565b61175a565b61031061060e366004612973565b6117f9565b61036b610621366004612b0f565b611823565b61036b610634366004612973565b6118ad565b6102d5611b7e565b61036b61064f366004612a32565b611b97565b6102d5610662366004612760565b611bb7565b610310610675366004612973565b611bd9565b610310610688366004612ba0565b611bec565b600154610310906001600160a01b031681565b61036b6106ae366004612c3c565b611c3f565b6106f66106c1366004612973565b60056020526000908152604090208054600182015460028301546003909301546001600160a01b039283169391929091169084565b6040516102df94939291906001600160a01b039485168152602081019390935292166040820152606081019190915260800190565b6102d560045481565b606060036040516020016107489190612d2a565b604051602081830303815290604052905090565b6000908152602081905260409020546001600160a01b031690565b600060008051602061318f83398151915261079181611df1565b610808898989898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8d018190048102820181019092528b815292508b91508a90819084018382808284376000920191909152508a9250611e45915050565b9998505050505050505050565b600061082e60008051602061312f833981519152611bb7565b905090565b60008051602061316f83398151915261084b81611df1565b6001600160a01b038381166000908152600560205260409020541661088b5760405162461bcd60e51b815260040161088290612db5565b60405180910390fd5b6040805180820182526001600160a01b03808616600090815260056020908152938120600201549091168252918101919091526108c9908490612186565b604080518082019091526001600160a01b03831681526000805160206130ef833981519152600090815260209081527f7373cba22413404e2328a4d29e64d2e6ec188b66fc2482ce280da4cc9e1010ec54600160a01b90046001600160601b031690820152610939908490612186565b6001600160a01b0383811660008181526005602052604080822060020180546001600160a01b0319169487169485179055517f46b03560e5440592db04cf536396abd2d0e1da05cd4bab259a01af406a8686ed9190a3505050565b60008051602061318f8339815191526109ac81611df1565b6000805b85811015610a8c578686828181106109ca576109ca612dde565b90506020020160208101906109df9190612973565b6001600160a01b0380821660009081526005602052604090205491935016610a195760405162461bcd60e51b815260040161088290612db5565b60405163e2bf56dd60e01b81526001600160a01b0386811660048301526024820186905283169063e2bf56dd90604401600060405180830381600087803b158015610a6357600080fd5b505af1158015610a77573d6000803e3d6000fd5b5050505080610a8590612e0a565b90506109b0565b50505050505050565b60008051602061314f833981519152610aad81611df1565b610ac560008051602061318f8339815191523361220e565b610ade60008051602061314f833981519152600061220e565b50565b600061082e60008051602061318f83398151915261075c565b60008051602061318f833981519152610b1281611df1565b6000846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b769190612e23565b90506000856001600160a01b031663258294106040518163ffffffff1660e01b8152600401600060405180830381865afa158015610bb8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610be09190810190612e40565b6001600160a01b03831660009081526006602052604080822090519293509091610c0b908490612eb7565b9081526040805160209281900383019020600089815292529020546001600160a01b031614610c96576001600160a01b038216600090815260066020526040908190209051610c5b908390612eb7565b9081526040805160209281900383018120600089815293529120546329ab51bf60e01b82526001600160a01b03166004820152602401610882565b306001600160a01b0316866001600160a01b03166379b989176040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d029190612e23565b6001600160a01b031614610d6457856001600160a01b031663f776bf1f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610d4b57600080fd5b505af1158015610d5f573d6000803e3d6000fd5b505050505b6000610d7d6000805160206130cf83398151915261075c565b60405163a237e94d60e01b81526001600160a01b0389811660048301529192509082169063a237e94d90602401602060405180830381865afa158015610dc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610deb9190612ed3565b610e4b57604051630ab322d960e01b81526001600160a01b038881166004830152821690630ab322d990602401600060405180830381600087803b158015610e3257600080fd5b505af1158015610e46573d6000803e3d6000fd5b505050505b610e558786612267565b60006001600160a01b0316876001600160a01b0316634fb3ccc56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec29190612e23565b6001600160a01b031603610ed957610ed987612430565b604080516080810182526001600160a01b0380861680835260208084018b81528a841685870190815260028054606088019081528f8716600090815260058652898120985189549089166001600160a01b0319918216178a55945160018a01559251918801805492909716919093161790945551600390940193909355815260069091528190209051889190610f70908590612eb7565b90815260408051918290036020908101832060008b8152915290812080546001600160a01b039485166001600160a01b0319918216179091556002805460018101825592527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace90910180548b851692168217905591871691907f65fe7f527479e9a13137b5a7af43efff08bb8e165ff5897ad493bb0362a690c190611018908a815260200190565b60405180910390a350505050505050565b60008051602061316f83398151915261104181611df1565b6001600160a01b03838116600090815260056020526040902054166110785760405162461bcd60e51b815260040161088290612db5565b600061109160008051602061312f83398151915261075c565b9050806001600160a01b0316836001600160a01b03161415801561111d5750604051634c9ba32160e11b81526001600160a01b038281166004830152851690639937464290602401602060405180830381865afa1580156110f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111a9190612ef5565b15155b1561115357611153846040518060400160405280846001600160a01b0316815260200160006001600160601b0316815250612186565b604080518082019091526001600160a01b038416815260008051602061312f833981519152600090815260209081527fe9cef8c56ab338574202f8c771407288ad562b542c26961a2a1a71f5e7e837c254600160a01b90046001600160601b0316908201526111c3908590612186565b50505050565b600061082e60008051602061312f83398151915261075c565b600061082e60008051602061314f83398151915261075c565b600061082e60008051602061318f833981519152611bb7565b60008051602061318f83398151915261122c81611df1565b60048290556040518281527feef7977251e5a99397bd5b40ed665b5ca8745df0f6bce919d03ce09c7eeb8d829060200160405180910390a15050565b600061082e6000805160206130cf83398151915261075c565b60008051602061318f83398151915261129981611df1565b6001600160a01b03848116600090815260056020526040902054166112d05760405162461bcd60e51b815260040161088290612db5565b60405163c47f002760e01b81526001600160a01b0385169063c47f0027906112fe9086908690600401612f0e565b600060405180830381600087803b15801561131857600080fd5b505af115801561132c573d6000803e3d6000fd5b5050505050505050565b600061082e60008051602061310f83398151915261075c565b6002818154811061135f57600080fd5b6000918252602090912001546001600160a01b0316905081565b600061082e60008051602061316f833981519152611bb7565b600061082e60008051602061316f83398151915261075c565b6060600280548060200260200160405190810160405280929190818152602001828054801561140357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113e5575b5050505050905090565b6000806114276000805160206130cf83398151915261075c565b6001600160a01b03166319ee073e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114889190612e23565b90506000816001600160a01b03166356e0a94b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ee9190612ef5565b9050805b80156116465760006001600160a01b03841663672383c4611514600185612f3d565b6040518263ffffffff1660e01b815260040161153291815260200190565b602060405180830381865afa15801561154f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115739190612e23565b6001600160a01b031663258294106040518163ffffffff1660e01b8152600401600060405180830381865afa1580156115b0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115d89190810190612e40565b6001600160a01b03881660009081526006602052604090819020905191925090611603908390612eb7565b9081526040805160209281900383019020600089815292529020546001600160a01b0316945084156116355750611646565b5061163f81612f50565b90506114f2565b50505092915050565b600061082e6000805160206130ef83398151915261075c565b60008051602061318f83398151915261168081611df1565b60008051602061318f83398151915283036116ce5760405162461bcd60e51b815260206004820152600e60248201526d2174776f207374657020666c6f7760901b6044820152606401610882565b6116d8838361220e565b505050565b60008051602061318f8339815191526116f581611df1565b6001600160a01b038481166000908152600560205260409020541661172c5760405162461bcd60e51b815260040161088290612db5565b604051635c26412360e11b81526001600160a01b0385169063b84c8246906112fe9086908690600401612f0e565b600060008051602061318f83398151915261177481611df1565b6117ed888888888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8c018190048102820181019092528a815292508a91508990819084018382808284376000920191909152506000199250611e45915050565b98975050505050505050565b60006118126000805160206130ef83398151915261075c565b905061181e8282610833565b919050565b60008051602061318f83398151915261183b81611df1565b6000805160206130ef8339815191528314158015611867575060008051602061312f8339815191528314155b6118a35760405162461bcd60e51b815260206004820152600d60248201526c63616e6e6f742075706461746560981b6044820152606401610882565b6116d883836125a3565b60008051602061316f8339815191526118c581611df1565b6001600160a01b0380831660009081526005602090815260409182902082516080810184528154851680825260018301549382019390935260028201549094169284019290925260039091015460608301526119335760405162461bcd60e51b815260040161088290612db5565b60015460405163ef54cefd60e01b81526001600160a01b0391821660048201529084169063ef54cefd90602401600060405180830381600087803b15801561197a57600080fd5b505af115801561198e573d6000803e3d6000fd5b505050506000600260016002805490506119a89190612f3d565b815481106119b8576119b8612dde565b9060005260206000200160009054906101000a90046001600160a01b031690508060028360600151815481106119f0576119f0612dde565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055606085015192841682526005905260409020600301556002805480611a3f57611a3f612f67565b60008281526020808220830160001990810180546001600160a01b031916905590920190925583516001600160a01b039081168352600690915260408083208151630258294160e41b81529151909392881692632582941092600480820193918290030181865afa158015611ab8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ae09190810190612e40565b604051611aed9190612eb7565b908152604080516020928190038301812085840151600090815290845282812080546001600160a01b03199081169091556001600160a01b03891680835260059095529281208054841681556001810182905560028101805490941690935560039092018290557fa40a7d97cd4243d8bcd193dc4d709afc3717750c5996d9e7216d1a5e0288c4949190a250505050565b600061082e6000805160206130ef833981519152611bb7565b611bb3828261040c6000805160206130ef83398151915261075c565b5050565b600090815260208190526040902054600160a01b90046001600160601b031690565b6000611be682600161140d565b92915050565b6001600160a01b0383166000908152600660205260408082209051611c12908590612eb7565b90815260408051602092819003830190206000948552909152909120546001600160a01b03169392505050565b6001546001600160a01b031615611c865760405162461bcd60e51b815260206004820152600b60248201526a1a5b9a5d1a585b1a5e995960aa1b6044820152606401610882565b6001600160a01b038616611ccb5760405162461bcd60e51b815260206004820152600c60248201526b5a45524f204144445245535360a01b6044820152606401610882565b600180546001600160a01b0319166001600160a01b0388161790556003611cf3888a83612fc3565b5062093a80600455611d1360008051602061318f8339815191528761220e565b611d2d60008051602061318f833981519152613fff6125a3565b611d4560008051602061316f8339815191528661220e565b611d5f60008051602061316f8339815191526119706125a3565b611d7760008051602061312f8339815191528561220e565b611d9060008051602061312f83398151915260206125a3565b611da86000805160206130ef8339815191528261220e565b611dc16000805160206130ef83398151915260606125a3565b611dd96000805160206130cf8339815191528461220e565b61132c60008051602061310f8339815191528361220e565b611dfa8161075c565b6001600160a01b0316336001600160a01b031614610ade5760405162461bcd60e51b815260206004820152600860248201526708585b1b1bddd95960c21b6044820152606401610882565b6000611e5e6000805160206130cf83398151915261075c565b6001600160a01b03166317bdd312878686306004546040518663ffffffff1660e01b8152600401611e93959493929190613084565b6020604051808303816000875af1158015611eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed69190612e23565b90506000816001600160a01b031663258294106040518163ffffffff1660e01b8152600401600060405180830381865afa158015611f18573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f409190810190612e40565b6001600160a01b03881660009081526006602052604080822090519293509091611f6b908490612eb7565b908152604080516020928190038301902060008a815292529020546001600160a01b031614611ff6576001600160a01b038716600090815260066020526040908190209051611fbb908390612eb7565b908152604080516020928190038301812060008a815293529120546329ab51bf60e01b82526001600160a01b03166004820152602401610882565b600061200f6000805160206130ef83398151915261075c565b905061201b8382612267565b61202483612430565b8315612034576120348385612607565b604080516080810182526001600160a01b03808b1680835260208084018c815286841685870190815260028054606088019081528b8716600090815260058652898120985189549089166001600160a01b0319918216178a55945160018a015592519188018054929097169190931617909455516003909401939093558152600690915281902090518491906120cb908590612eb7565b90815260408051918290036020908101832060008c8152915290812080546001600160a01b039485166001600160a01b0319918216179091556002805460018101825592527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace909101805487851692168217905591831691907f65fe7f527479e9a13137b5a7af43efff08bb8e165ff5897ad493bb0362a690c190612173908b815260200190565b60405180910390a3505095945050505050565b80516001600160a01b031615611bb35780516020820151604051632cf7fd8560e01b81526001600160a01b0392831660048201526001600160601b03909116602482015290831690632cf7fd85906044015b600060405180830381600087803b1580156121f257600080fd5b505af1158015612206573d6000803e3d6000fd5b505050505050565b60008281526020819052604080822080546001600160a01b0319166001600160a01b0385169081179091559051909184917f8c42303edb98aad4f3e4f1a15363dda2db344e9fd66ecff9acf62f770c474fa39190a35050565b60008051602061318f83398151915260009081526020908152604080518082019091527f624ef3c7d6e705ccd5cf44dbc65d64ed664f093a9431ff0584a58a9846ac3d0d546001600160a01b0381168252600160a01b90046001600160601b0316918101919091526122da908390612186565b60008051602061316f83398151915260009081526020908152604080518082019091527f4829c109b61fea6781bd77c95626b114c88442499c00e59076c6d85e749d9674546001600160a01b0381168252600160a01b90046001600160601b03169181019190915261234d908390612186565b60008051602061312f83398151915260009081526020908152604080518082019091527fe9cef8c56ab338574202f8c771407288ad562b542c26961a2a1a71f5e7e837c2546001600160a01b0381168252600160a01b90046001600160601b0316918101919091526123c0908390612186565b604080518082019091526001600160a01b03821681526000805160206130ef833981519152600090815260209081527f7373cba22413404e2328a4d29e64d2e6ec188b66fc2482ce280da4cc9e1010ec54600160a01b90046001600160601b031690820152611bb3908390612186565b600061244960008051602061310f83398151915261075c565b90506001600160a01b03811615611bb3576040516354be77d160e11b8152306004820152600860248201526001600160a01b0383169063a97cefa290604401600060405180830381600087803b1580156124a257600080fd5b505af11580156124b6573d6000803e3d6000fd5b50506040516371da8a8d60e01b81526001600160a01b038481166004830152851692506371da8a8d9150602401600060405180830381600087803b1580156124fd57600080fd5b505af1158015612511573d6000803e3d6000fd5b505060405163e2bf56dd60e01b8152306004820152600860248201526001600160a01b038516925063e2bf56dd9150604401600060405180830381600087803b15801561255d57600080fd5b505af1158015612571573d6000803e3d6000fd5b50506040516312b5ad0160e11b81526001600160a01b0385811660048301528416925063256b5a0291506024016121d8565b6000828152602081815260409182902080546001600160a01b0316600160a01b6001600160601b03861602179055905182815283917ff8a7e043f1e7310711bc663ed81ae8855aa474348c57a6461b9f52a4fa88a94d910160405180910390a25050565b6040516354be77d160e11b815230600482015261010060248201526001600160a01b0383169063a97cefa290604401600060405180830381600087803b15801561265057600080fd5b505af1158015612664573d6000803e3d6000fd5b50506040516337f00e8f60e11b8152600481018490526001600160a01b0385169250636fe01d1e9150602401600060405180830381600087803b1580156126aa57600080fd5b505af11580156126be573d6000803e3d6000fd5b505060405163e2bf56dd60e01b815230600482015261010060248201526001600160a01b038516925063e2bf56dd91506044016121d8565b60005b838110156127115781810151838201526020016126f9565b50506000910152565b600081518084526127328160208601602086016126f6565b601f01601f19169290920160200192915050565b602081526000612759602083018461271a565b9392505050565b60006020828403121561277257600080fd5b5035919050565b6001600160a01b0381168114610ade57600080fd5b60008083601f8401126127a057600080fd5b50813567ffffffffffffffff8111156127b857600080fd5b6020830191508360208285010111156127d057600080fd5b9250929050565b600080600080600080600060a0888a0312156127f257600080fd5b87356127fd81612779565b965060208801359550604088013567ffffffffffffffff8082111561282157600080fd5b61282d8b838c0161278e565b909750955060608a013591508082111561284657600080fd5b506128538a828b0161278e565b989b979a50959894979596608090950135949350505050565b6000806040838503121561287f57600080fd5b823561288a81612779565b9150602083013561289a81612779565b809150509250929050565b600080600080606085870312156128bb57600080fd5b843567ffffffffffffffff808211156128d357600080fd5b818701915087601f8301126128e757600080fd5b8135818111156128f657600080fd5b8860208260051b850101111561290b57600080fd5b6020928301965094505085013561292181612779565b9396929550929360400135925050565b60008060006060848603121561294657600080fd5b833561295181612779565b925060208401359150604084013561296881612779565b809150509250925092565b60006020828403121561298557600080fd5b813561275981612779565b6000806000604084860312156129a557600080fd5b83356129b081612779565b9250602084013567ffffffffffffffff8111156129cc57600080fd5b6129d88682870161278e565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b81811015612a265783516001600160a01b031683529284019291840191600101612a01565b50909695505050505050565b60008060408385031215612a4557600080fd5b8235612a5081612779565b946020939093013593505050565b60008060408385031215612a7157600080fd5b82359150602083013561289a81612779565b60008060008060008060808789031215612a9c57600080fd5b8635612aa781612779565b955060208701359450604087013567ffffffffffffffff80821115612acb57600080fd5b612ad78a838b0161278e565b90965094506060890135915080821115612af057600080fd5b50612afd89828a0161278e565b979a9699509497509295939492505050565b60008060408385031215612b2257600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612b7057612b70612b31565b604052919050565b600067ffffffffffffffff821115612b9257612b92612b31565b50601f01601f191660200190565b600080600060608486031215612bb557600080fd5b8335612bc081612779565b9250602084013567ffffffffffffffff811115612bdc57600080fd5b8401601f81018613612bed57600080fd5b8035612c00612bfb82612b78565b612b47565b818152876020838501011115612c1557600080fd5b81602084016020830137600060208383010152809450505050604084013590509250925092565b60008060008060008060008060e0898b031215612c5857600080fd5b883567ffffffffffffffff811115612c6f57600080fd5b612c7b8b828c0161278e565b9099509750506020890135612c8f81612779565b95506040890135612c9f81612779565b94506060890135612caf81612779565b93506080890135612cbf81612779565b925060a0890135612ccf81612779565b915060c0890135612cdf81612779565b809150509295985092959890939650565b600181811c90821680612d0457607f821691505b602082108103612d2457634e487b7160e01b600052602260045260246000fd5b50919050565b6000808354612d3881612cf0565b60018281168015612d505760018114612d6557612d94565b60ff1984168752821515830287019450612d94565b8760005260208060002060005b85811015612d8b5781548a820152908401908201612d72565b50505082870194505b50506c102937b6329026b0b730b3b2b960991b83525050600d019392505050565b6020808252600f908201526e1d985d5b1d081b9bdd081859191959608a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201612e1c57612e1c612df4565b5060010190565b600060208284031215612e3557600080fd5b815161275981612779565b600060208284031215612e5257600080fd5b815167ffffffffffffffff811115612e6957600080fd5b8201601f81018413612e7a57600080fd5b8051612e88612bfb82612b78565b818152856020838501011115612e9d57600080fd5b612eae8260208301602086016126f6565b95945050505050565b60008251612ec98184602087016126f6565b9190910192915050565b600060208284031215612ee557600080fd5b8151801515811461275957600080fd5b600060208284031215612f0757600080fd5b5051919050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b81810381811115611be657611be6612df4565b600081612f5f57612f5f612df4565b506000190190565b634e487b7160e01b600052603160045260246000fd5b601f8211156116d857600081815260208120601f850160051c81016020861015612fa45750805b601f850160051c820191505b8181101561220657828155600101612fb0565b67ffffffffffffffff831115612fdb57612fdb612b31565b612fef83612fe98354612cf0565b83612f7d565b6000601f841160018114613023576000851561300b5750838201355b600019600387901b1c1916600186901b17835561307d565b600083815260209020601f19861690835b828110156130545786850135825560209485019460019092019101613034565b50868210156130715760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600060018060a01b03808816835260a060208401526130a660a084018861271a565b83810360408501526130b8818861271a565b959091166060840152505060800152939250505056fe8c53ae70b0aa86068023c633abcdda89c433a8a76949ea111f756c171fa23663e36275342c99ede79a003741503250226904c6a298f8766fdff5fe056b171b701629cfbd564ce33f37c84741f82a28dcd353c97db5819722102f8704671969be4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de01c38a7d6f5f636faa4e9982efc7dc023d5598f663e3a2a2d4008a5ff3c2de210a0e28cff0ad91cc255a2c6f970ad7d0cc9855e3f9229ce313219b7b468394589409903de1e6fd852dfc61c9dacb48196c48535b60e25abf92acc92dd689078da264697066735822122071530ed176281faa365249f326cb8bb2abd880405e8662ed67c067227fe1b6be64736f6c63430008120033000000000000000000000000775f09d6f3c8d2182dfa8bce8628acf51105653c
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100a95760003560e01c8063511abc9011610071578063511abc9014610162578063861ceb6914610175578063862a179e1461019c578063b96ea12d146101c3578063c97bbd3f1461023c578063ec6d26211461024f57600080fd5b80631fe833b1146100ae57806325829410146100e857806346c715fa146100fd57806347cbf63e146101285780634c73e7791461013b575b600080fd5b6100d57f26555a2400e5f7a7dc5f4794e1040ade6c5cec3c38b80ceaff00502a62e4b65881565b6040519081526020015b60405180910390f35b6100f0610276565b6040516100df9190610aaa565b600054610110906001600160a01b031681565b6040516001600160a01b0390911681526020016100df565b610110610136366004610b4e565b610304565b6101107f000000000000000000000000775f09d6f3c8d2182dfa8bce8628acf51105653c81565b6100d5610170366004610c18565b6103bd565b6100d57f8975f7470d8885db19a03ab19387b0d032119bec5e894c777643a8eedb564af481565b6100d57f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de81565b6102096101d1366004610cda565b600260208190526000918252604090912080546001820154928201546003909201546001600160a01b03918216938216928216911684565b604080516001600160a01b03958616815293851660208501529184169183019190915290911660608201526080016100df565b61011061024a366004610cf3565b6103f2565b6100d57f15cf1584ec2aa3ee42f6f37421830cb25dd95e2e2b575c279686a2b05aeeaa3a81565b6001805461028390610d5b565b80601f01602080910402602001604051908101604052809291908181526020018280546102af90610d5b565b80156102fc5780601f106102d1576101008083540402835291602001916102fc565b820191906000526020600020905b8154815290600101906020018083116102df57829003601f168201915b505050505081565b600061030e610987565b60405163f796e58760e01b81529091506001600160a01b0382169063f796e5879061034b908c908c908c908c908c908c908c908c90600401610d95565b600060405180830381600087803b15801561036557600080fd5b505af1158015610379573d6000803e3d6000fd5b50506040516001600160a01b03841692507f078e7fe644f074748ec021662e935d12aa303bd3e32403f200c621b7cd8c26109150600090a298975050505050505050565b60008282466040516020016103d493929190610dfa565b60405160208183030381529060405280519060200120905092915050565b60008061043686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506103bd915050565b6000818152600260205260409020549091506001600160a01b0316156104935760405162461bcd60e51b815260206004820152600e60248201526d70726f6a6563742065786973747360901b604482015260640160405180910390fd5b60006104be7f8975f7470d8885db19a03ab19387b0d032119bec5e894c777643a8eedb564af46109a2565b6001600160a01b031663e2eb36ee88886040516020016104df929190610e39565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161050a9190610aaa565b6020604051808303816000875af1158015610529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054d9190610e57565b9050600061057a7f15cf1584ec2aa3ee42f6f37421830cb25dd95e2e2b575c279686a2b05aeeaa3a6109a2565b60405163f37bb0ff60e01b81523060048201526001600160a01b038881166024830152919091169063f37bb0ff906044016020604051808303816000875af11580156105ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ee9190610e57565b9050600061061b7f26555a2400e5f7a7dc5f4794e1040ade6c5cec3c38b80ceaff00502a62e4b6586109a2565b6001600160a01b03908116906328d34c90908816610639578861063b565b875b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303816000875af1158015610681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a59190610e57565b90506106af610987565b9450846001600160a01b031663f796e5878a8a8a8a6106ed7f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de6109a2565b8989896040518963ffffffff1660e01b8152600401610713989796959493929190610d95565b600060405180830381600087803b15801561072d57600080fd5b505af1158015610741573d6000803e3d6000fd5b5050604051632c2a72d560e01b81526001600160a01b0388811660048301526001602483015286169250632c2a72d59150604401600060405180830381600087803b15801561078f57600080fd5b505af11580156107a3573d6000803e3d6000fd5b50506040516334e2fffd60e21b81526001600160a01b038a811660048301528616925063d38bfff49150602401600060405180830381600087803b1580156107ea57600080fd5b505af11580156107fe573d6000803e3d6000fd5b5050604051635aa1a81f60e11b81526001600160a01b0388811660048301528516925063b543503e9150602401600060405180830381600087803b15801561084557600080fd5b505af1158015610859573d6000803e3d6000fd5b5050604051639b3b695560e01b81526001600160a01b038a8116600483015285169250639b3b69559150602401600060405180830381600087803b1580156108a057600080fd5b505af11580156108b4573d6000803e3d6000fd5b5050604080516080810182526001600160a01b03808a1680835288821660208085019182528984168587019081528985166060870190815260008e8152600293849052888120975188549088166001600160a01b03199182161789559451600189018054918916918716919091179055915192870180549387169385169390931790925590516003909501805495909416949091169390931790915591519193508792507f8a7d5af3ee2f0fbd1d46dea22a1f5283b0f0716d637b79e7f721f87e0f94c1eb91a350505050949350505050565b6000805461099d906001600160a01b0316610a34565b905090565b6040516321f8a72160e01b8152600481018290526000907f000000000000000000000000775f09d6f3c8d2182dfa8bce8628acf51105653c6001600160a01b0316906321f8a72190602401602060405180830381865afa158015610a0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2e9190610e57565b92915050565b6000808260601b9050604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528160148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f0949350505050565b60005b83811015610aa1578181015183820152602001610a89565b50506000910152565b6020815260008251806020840152610ac9816040850160208701610a86565b601f01601f19169190910160400192915050565b60008083601f840112610aef57600080fd5b50813567ffffffffffffffff811115610b0757600080fd5b602083019150836020828501011115610b1f57600080fd5b9250929050565b6001600160a01b0381168114610b3b57600080fd5b50565b8035610b4981610b26565b919050565b60008060008060008060008060e0898b031215610b6a57600080fd5b883567ffffffffffffffff811115610b8157600080fd5b610b8d8b828c01610add565b9099509750506020890135610ba181610b26565b95506040890135610bb181610b26565b94506060890135610bc181610b26565b93506080890135610bd181610b26565b925060a0890135610be181610b26565b915060c0890135610bf181610b26565b809150509295985092959890939650565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610c2b57600080fd5b823567ffffffffffffffff80821115610c4357600080fd5b818501915085601f830112610c5757600080fd5b813581811115610c6957610c69610c02565b604051601f8201601f19908116603f01168101908382118183101715610c9157610c91610c02565b81604052828152886020848701011115610caa57600080fd5b826020860160208301376000602084830101528096505050505050610cd160208401610b3e565b90509250929050565b600060208284031215610cec57600080fd5b5035919050565b60008060008060608587031215610d0957600080fd5b843567ffffffffffffffff811115610d2057600080fd5b610d2c87828801610add565b9095509350506020850135610d4081610b26565b91506040850135610d5081610b26565b939692955090935050565b600181811c90821680610d6f57607f821691505b602082108103610d8f57634e487b7160e01b600052602260045260246000fd5b50919050565b60e081528760e08201526000610100898b828501376000838b018201526001600160a01b03988916602084015296881660408301525093861660608501529185166080840152841660a083015290921660c0830152601f909201601f19160101919050565b60008451610e0c818460208901610a86565b60609490941b6bffffffffffffffffffffffff191691909301908152601481019190915260340192915050565b818382376820526567697374727960b81b9101908152600901919050565b600060208284031215610e6957600080fd5b8151610e7481610b26565b939250505056fea26469706673582212205090e5a7e9a2ede6d6f8f3f05124374882a5864fd9c74a25bfcb786b1ec4a9f464736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000775f09d6f3c8d2182dfa8bce8628acf51105653c
-----Decoded View---------------
Arg [0] : _addressProvider (address): 0x775F09d6f3c8D2182DFA8bce8628acf51105653c
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000775f09d6f3c8d2182dfa8bce8628acf51105653c
Deployed Bytecode Sourcemap
197572:5536:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;198104:93;;198162:35;198104:93;;;;;160:25:1;;;148:2;133:18;198104:93:0;;;;;;;;198391:34;;;:::i;:::-;;;;;;;:::i;32022:23::-;;;;;-1:-1:-1;;;;;32022:23:0;;;;;;-1:-1:-1;;;;;1016:32:1;;;998:51;;986:2;971:18;32022:23:0;852:203:1;199246:623:0;;;;;;:::i;:::-;;:::i;198434:48::-;;;;;202522:215;;;;;;:::i;:::-;;:::i;198310:72::-;;198353:29;198310:72;;198204:52;;198237:19;198204:52;;198491:43;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;198491:43:0;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4563:15:1;;;4545:34;;4615:15;;;4610:2;4595:18;;4588:43;4667:15;;;4647:18;;;4640:43;;;;4719:15;;;4714:2;4699:18;;4692:43;4494:3;4479:19;198491:43:0;4276:465:1;200350:1957:0;;;;;;:::i;:::-;;:::i;197956:85::-;;198010:31;197956:85;;198391:34;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;199246:623::-;199519:20;199567:8;:6;:8::i;:::-;199588:227;;-1:-1:-1;;;199588:227:0;;199552:23;;-1:-1:-1;;;;;;199588:36:0;;;;;:227;;199639:12;;;;199666:11;;199692;;199718:7;;199740:9;;199764:11;;199790:14;;199588:227;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;199833:28:0;;-1:-1:-1;;;;;199833:28:0;;;-1:-1:-1;199833:28:0;;-1:-1:-1;199833:28:0;;;199246:623;;;;;;;;;;:::o;202522:215::-;202640:7;202694:5;202701:11;202714:13;202677:51;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;202667:62;;;;;;202660:69;;202522:215;;;;:::o;200350:1957::-;200495:20;200528:11;200542:32;200555:5;;200542:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;200562:11:0;;-1:-1:-1;200542:12:0;;-1:-1:-1;;200542:32:0:i;:::-;200630:1;200593:13;;;:8;:13;;;;;:25;200528:46;;-1:-1:-1;;;;;;200593:25:0;:39;200585:66;;;;-1:-1:-1;;;200585:66:0;;7469:2:1;200585:66:0;;;7451:21:1;7508:2;7488:18;;;7481:30;-1:-1:-1;;;7527:18:1;;;7520:44;7581:18;;200585:66:0;;;;;;;;200715:17;200765:38;198353:29;200765:20;:38::i;:::-;-1:-1:-1;;;;;200735:97:0;;200857:5;;200840:36;;;;;;;;;:::i;:::-;;;;;;;;;;;;;200735:143;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;200715:163;;200891:19;200945:40;198010:31;200945:20;:40::i;:::-;200913:125;;-1:-1:-1;;;200913:125:0;;201019:4;200913:125;;;8475:34:1;-1:-1:-1;;;;;8545:15:1;;;8525:18;;;8518:43;200913:97:0;;;;;;;8410:18:1;;200913:125:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;200891:147;;201149:22;201209:44;198162:35;201209:20;:44::i;:::-;-1:-1:-1;;;;;201174:107:0;;;;;;201300:25;;:53;;201342:11;201300:53;;;201328:11;201300:53;201174:194;;-1:-1:-1;;;;;;201174:194:0;;;;;;;-1:-1:-1;;;;;1016:32:1;;;201174:194:0;;;998:51:1;971:18;;201174:194:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;201149:219;;201446:8;:6;:8::i;:::-;201431:23;;201479:12;-1:-1:-1;;;;;201467:36:0;;201518:5;;201538:11;201564;201590:28;198237:19;201590:20;:28::i;:::-;201633:9;201657:11;201683:14;201467:241;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;201801:51:0;;-1:-1:-1;;;201801:51:0;;-1:-1:-1;;;;;8758:32:1;;;201801:51:0;;;8740::1;201847:4:0;8807:18:1;;;8800:50;201801:31:0;;;-1:-1:-1;201801:31:0;;-1:-1:-1;8713:18:1;;201801:51:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;201863:51:0;;-1:-1:-1;;;201863:51:0;;-1:-1:-1;;;;;1016:32:1;;;201863:51:0;;;998::1;201863:38:0;;;-1:-1:-1;201863:38:0;;-1:-1:-1;971:18:1;;201863:51:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;201927:53:0;;-1:-1:-1;;;201927:53:0;;-1:-1:-1;;;;;1016:32:1;;;201927:53:0;;;998:51:1;201927:39:0;;;-1:-1:-1;201927:39:0;;-1:-1:-1;971:18:1;;201927:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;201991:56:0;;-1:-1:-1;;;201991:56:0;;-1:-1:-1;;;;;1016:32:1;;;201991:56:0;;;998:51:1;201991:43:0;;;-1:-1:-1;201991:43:0;;-1:-1:-1;971:18:1;;201991:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;202076:176:0;;;;;;;;-1:-1:-1;;;;;202076:176:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;202060:13:0;;;:8;:13;;;;;;;:192;;;;;;;-1:-1:-1;;;;;;202060:192:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;202270:29;;202076:176;;-1:-1:-1;202069:3:0;;-1:-1:-1;202270:29:0;;;200517:1790;;;;200350:1957;;;;;;:::o;32193:95::-;32237:7;32271:8;;32264:16;;-1:-1:-1;;;;;32271:8:0;32264:6;:16::i;:::-;32257:23;;32193:95;:::o;202926:179::-;203032:65;;-1:-1:-1;;;203032:65:0;;;;;160:25:1;;;202992:7:0;;203057:23;-1:-1:-1;;;;;203032:60:0;;;;133:18:1;;203032:65:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;203012:85;202926:179;-1:-1:-1;;202926:179:0:o;32431:798::-;32508:20;32649;32680:9;32672:18;;32649:41;;32783:4;32777:11;-1:-1:-1;;;32827:10:0;32802:135;32981:12;32974:4;32962:10;32958:21;32951:43;-1:-1:-1;;;33049:4:0;33037:10;33033:21;33008:146;33206:4;33194:10;33191:1;33184:27;33168:43;32431:798;-1:-1:-1;;;;32431:798:0:o;196:250:1:-;281:1;291:113;305:6;302:1;299:13;291:113;;;381:11;;;375:18;362:11;;;355:39;327:2;320:10;291:113;;;-1:-1:-1;;438:1:1;420:16;;413:27;196:250::o;451:396::-;600:2;589:9;582:21;563:4;632:6;626:13;675:6;670:2;659:9;655:18;648:34;691:79;763:6;758:2;747:9;743:18;738:2;730:6;726:15;691:79;:::i;:::-;831:2;810:15;-1:-1:-1;;806:29:1;791:45;;;;838:2;787:54;;451:396;-1:-1:-1;;451:396:1:o;1060:348::-;1112:8;1122:6;1176:3;1169:4;1161:6;1157:17;1153:27;1143:55;;1194:1;1191;1184:12;1143:55;-1:-1:-1;1217:20:1;;1260:18;1249:30;;1246:50;;;1292:1;1289;1282:12;1246:50;1329:4;1321:6;1317:17;1305:29;;1381:3;1374:4;1365:6;1357;1353:19;1349:30;1346:39;1343:59;;;1398:1;1395;1388:12;1343:59;1060:348;;;;;:::o;1413:131::-;-1:-1:-1;;;;;1488:31:1;;1478:42;;1468:70;;1534:1;1531;1524:12;1468:70;1413:131;:::o;1549:134::-;1617:20;;1646:31;1617:20;1646:31;:::i;:::-;1549:134;;;:::o;1688:1255::-;1813:6;1821;1829;1837;1845;1853;1861;1869;1922:3;1910:9;1901:7;1897:23;1893:33;1890:53;;;1939:1;1936;1929:12;1890:53;1979:9;1966:23;2012:18;2004:6;2001:30;1998:50;;;2044:1;2041;2034:12;1998:50;2083:59;2134:7;2125:6;2114:9;2110:22;2083:59;:::i;:::-;2161:8;;-1:-1:-1;2057:85:1;-1:-1:-1;;2246:2:1;2231:18;;2218:32;2259:31;2218:32;2259:31;:::i;:::-;2309:5;-1:-1:-1;2366:2:1;2351:18;;2338:32;2379:33;2338:32;2379:33;:::i;:::-;2431:7;-1:-1:-1;2490:2:1;2475:18;;2462:32;2503:33;2462:32;2503:33;:::i;:::-;2555:7;-1:-1:-1;2614:3:1;2599:19;;2586:33;2628;2586;2628;:::i;:::-;2680:7;-1:-1:-1;2739:3:1;2724:19;;2711:33;2753;2711;2753;:::i;:::-;2805:7;-1:-1:-1;2864:3:1;2849:19;;2836:33;2878;2836;2878;:::i;:::-;2930:7;2920:17;;;1688:1255;;;;;;;;;;;:::o;2948:127::-;3009:10;3004:3;3000:20;2997:1;2990:31;3040:4;3037:1;3030:15;3064:4;3061:1;3054:15;3080:1006;3158:6;3166;3219:2;3207:9;3198:7;3194:23;3190:32;3187:52;;;3235:1;3232;3225:12;3187:52;3275:9;3262:23;3304:18;3345:2;3337:6;3334:14;3331:34;;;3361:1;3358;3351:12;3331:34;3399:6;3388:9;3384:22;3374:32;;3444:7;3437:4;3433:2;3429:13;3425:27;3415:55;;3466:1;3463;3456:12;3415:55;3502:2;3489:16;3524:2;3520;3517:10;3514:36;;;3530:18;;:::i;:::-;3605:2;3599:9;3573:2;3659:13;;-1:-1:-1;;3655:22:1;;;3679:2;3651:31;3647:40;3635:53;;;3703:18;;;3723:22;;;3700:46;3697:72;;;3749:18;;:::i;:::-;3789:10;3785:2;3778:22;3824:2;3816:6;3809:18;3866:7;3859:4;3854:2;3850;3846:11;3842:22;3839:35;3836:55;;;3887:1;3884;3877:12;3836:55;3947:2;3940:4;3936:2;3932:13;3925:4;3917:6;3913:17;3900:50;3994:1;3987:4;3982:2;3974:6;3970:15;3966:26;3959:37;4015:6;4005:16;;;;;;;4040:40;4074:4;4063:9;4059:20;4040:40;:::i;:::-;4030:50;;3080:1006;;;;;:::o;4091:180::-;4150:6;4203:2;4191:9;4182:7;4178:23;4174:32;4171:52;;;4219:1;4216;4209:12;4171:52;-1:-1:-1;4242:23:1;;4091:180;-1:-1:-1;4091:180:1:o;4746:687::-;4835:6;4843;4851;4859;4912:2;4900:9;4891:7;4887:23;4883:32;4880:52;;;4928:1;4925;4918:12;4880:52;4968:9;4955:23;5001:18;4993:6;4990:30;4987:50;;;5033:1;5030;5023:12;4987:50;5072:59;5123:7;5114:6;5103:9;5099:22;5072:59;:::i;:::-;5150:8;;-1:-1:-1;5046:85:1;-1:-1:-1;;5235:2:1;5220:18;;5207:32;5248:31;5207:32;5248:31;:::i;:::-;5298:5;-1:-1:-1;5355:2:1;5340:18;;5327:32;5368:33;5327:32;5368:33;:::i;:::-;4746:687;;;;-1:-1:-1;4746:687:1;;-1:-1:-1;;4746:687:1:o;5438:380::-;5517:1;5513:12;;;;5560;;;5581:61;;5635:4;5627:6;5623:17;5613:27;;5581:61;5688:2;5680:6;5677:14;5657:18;5654:38;5651:161;;5734:10;5729:3;5725:20;5722:1;5715:31;5769:4;5766:1;5759:15;5797:4;5794:1;5787:15;5651:161;;5438:380;;;:::o;5823:937::-;6150:3;6139:9;6132:22;6191:6;6185:3;6174:9;6170:19;6163:35;6113:4;6217:3;6270:6;6262;6257:2;6246:9;6242:18;6229:48;6326:1;6297:22;;;6293:31;;6286:42;-1:-1:-1;;;;;6475:15:1;;;6468:4;6453:20;;6446:45;6527:15;;;6522:2;6507:18;;6500:43;-1:-1:-1;6579:15:1;;;6574:2;6559:18;;6552:43;6632:15;;;6626:3;6611:19;;6604:44;6685:15;;6426:3;6664:19;;6657:44;6738:15;;;6732:3;6717:19;;6710:44;6389:2;6368:15;;;-1:-1:-1;;6364:29:1;6349:45;6345:54;;5823:937;-1:-1:-1;5823:937:1:o;6765:497::-;6952:3;6990:6;6984:13;7006:66;7065:6;7060:3;7053:4;7045:6;7041:17;7006:66;:::i;:::-;7141:2;7137:15;;;;-1:-1:-1;;7133:53:1;7094:16;;;;7119:68;;;7214:2;7203:14;;7196:30;;;;7253:2;7242:14;;6765:497;-1:-1:-1;;6765:497:1:o;7610:392::-;7896:6;7888;7883:3;7870:33;-1:-1:-1;;;7922:16:1;;7947:23;;;7994:1;7986:10;;7610:392;-1:-1:-1;7610:392:1:o;8007:251::-;8077:6;8130:2;8118:9;8109:7;8105:23;8101:32;8098:52;;;8146:1;8143;8136:12;8098:52;8178:9;8172:16;8197:31;8222:5;8197:31;:::i;:::-;8247:5;8007:251;-1:-1:-1;;;8007:251:1:o
Swarm Source
ipfs://71530ed176281faa365249f326cb8bb2abd880405e8662ed67c067227fe1b6be
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.