creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\n// solhint-disable-next-line compiler-version\npragma solidity >=0.4.24 <0.8.0;\n\nimport \"../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n\n    /**\n     * @dev Indicates that the contract has been initialized.\n     */\n    bool private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Modifier to protect an initializer function from being invoked twice.\n     */\n    modifier initializer() {\n        require(_initializing || _isConstructor() || !_initialized, \"Initializable: contract is already initialized\");\n\n        bool isTopLevelCall = !_initializing;\n        if (isTopLevelCall) {\n            _initializing = true;\n            _initialized = true;\n        }\n\n        _;\n\n        if (isTopLevelCall) {\n            _initializing = false;\n        }\n    }\n\n    /// @dev Returns true if and only if the function is running in the constructor\n    function _isConstructor() private view returns (bool) {\n        return !AddressUpgradeable.isContract(address(this));\n    }\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n\n        uint256 size;\n        // solhint-disable-next-line no-inline-assembly\n        assembly { size := extcodesize(account) }\n        return size > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n        (bool success, ) = recipient.call{ value: amount }(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain`call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n      return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        require(isContract(target), \"Address: call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.call{ value: value }(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length > 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                // solhint-disable-next-line no-inline-assembly\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a >= b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow, so we distribute\n        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);\n    }\n}\n"
      },
      "@openzeppelin/contracts/math/SafeMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMath {\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        uint256 c = a + b;\n        if (c < a) return (false, 0);\n        return (true, c);\n    }\n\n    /**\n     * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b > a) return (false, 0);\n        return (true, a - b);\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n        // benefit is lost if 'b' is also tested.\n        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n        if (a == 0) return (true, 0);\n        uint256 c = a * b;\n        if (c / a != b) return (false, 0);\n        return (true, c);\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b == 0) return (false, 0);\n        return (true, a / b);\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b == 0) return (false, 0);\n        return (true, a % b);\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `+` operator.\n     *\n     * Requirements:\n     *\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 c = a + b;\n        require(c >= a, \"SafeMath: addition overflow\");\n        return c;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b <= a, \"SafeMath: subtraction overflow\");\n        return a - b;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `*` operator.\n     *\n     * Requirements:\n     *\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (a == 0) return 0;\n        uint256 c = a * b;\n        require(c / a == b, \"SafeMath: multiplication overflow\");\n        return c;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity's `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b > 0, \"SafeMath: division by zero\");\n        return a / b;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting when dividing by zero.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b > 0, \"SafeMath: modulo by zero\");\n        return a % b;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {trySub}.\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b <= a, errorMessage);\n        return a - b;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {tryDiv}.\n     *\n     * Counterpart to Solidity's `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b > 0, errorMessage);\n        return a / b;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting with custom message when dividing by zero.\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {tryMod}.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b > 0, errorMessage);\n        return a % b;\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\nimport \"../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using SafeMath for uint256;\n    using Address for address;\n\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        // solhint-disable-next-line max-line-length\n        require((value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).add(value);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).sub(value, \"SafeERC20: decreased allowance below zero\");\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) { // Return data is optional\n            // solhint-disable-next-line max-line-length\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n\n        uint256 size;\n        // solhint-disable-next-line no-inline-assembly\n        assembly { size := extcodesize(account) }\n        return size > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n        (bool success, ) = recipient.call{ value: amount }(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain`call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n      return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        require(isContract(target), \"Address: call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.call{ value: value }(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length > 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                // solhint-disable-next-line no-inline-assembly\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}\n"
      },
      "contracts/base/inheritance/ControllableInit.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"./GovernableInit.sol\";\n\n// A clone of Governable supporting the Initializable interface and pattern\ncontract ControllableInit is GovernableInit {\n\n  constructor() public {\n  }\n\n  function initialize(address _storage) public override initializer {\n    GovernableInit.initialize(_storage);\n  }\n\n  modifier onlyController() {\n    require(Storage(_storage()).isController(msg.sender), \"Not a controller\");\n    _;\n  }\n\n  modifier onlyControllerOrGovernance(){\n    require((Storage(_storage()).isController(msg.sender) || Storage(_storage()).isGovernance(msg.sender)),\n      \"The caller must be controller or governance\");\n    _;\n  }\n\n  function controller() public view returns (address) {\n    return Storage(_storage()).controller();\n  }\n}\n"
      },
      "contracts/base/inheritance/GovernableInit.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"../upgradability/ReentrancyGuardUpgradeable.sol\";\nimport \"./Storage.sol\";\n\n// A clone of Governable supporting the Initializable interface and pattern\ncontract GovernableInit is ReentrancyGuardUpgradeable {\n\n  bytes32 internal constant _STORAGE_SLOT = 0xa7ec62784904ff31cbcc32d09932a58e7f1e4476e1d041995b37c917990b16dc;\n\n  modifier onlyGovernance() {\n    require(Storage(_storage()).isGovernance(msg.sender), \"Not governance\");\n    _;\n  }\n\n  constructor() public {\n    assert(_STORAGE_SLOT == bytes32(uint256(keccak256(\"eip1967.governableInit.storage\")) - 1));\n  }\n\n  function initialize(address _store) public virtual initializer {\n    _setStorage(_store);\n    ReentrancyGuardUpgradeable.initialize();\n  }\n\n  function _setStorage(address newStorage) private {\n    bytes32 slot = _STORAGE_SLOT;\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      sstore(slot, newStorage)\n    }\n  }\n\n  function setStorage(address _store) public onlyGovernance {\n    require(_store != address(0), \"new storage shouldn't be empty\");\n    _setStorage(_store);\n  }\n\n  function _storage() internal view returns (address str) {\n    bytes32 slot = _STORAGE_SLOT;\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      str := sload(slot)\n    }\n  }\n\n  function governance() public view returns (address) {\n    return Storage(_storage()).governance();\n  }\n}\n"
      },
      "contracts/base/inheritance/Storage.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ncontract Storage {\n\n  address public governance;\n  address public controller;\n\n  constructor() public {\n    governance = msg.sender;\n  }\n\n  modifier onlyGovernance() {\n    require(isGovernance(msg.sender), \"Not governance\");\n    _;\n  }\n\n  function setGovernance(address _governance) public onlyGovernance {\n    require(_governance != address(0), \"new governance shouldn't be empty\");\n    governance = _governance;\n  }\n\n  function setController(address _controller) public onlyGovernance {\n    require(_controller != address(0), \"new controller shouldn't be empty\");\n    controller = _controller;\n  }\n\n  function isGovernance(address account) public view returns (bool) {\n    return account == governance;\n  }\n\n  function isController(address account) public view returns (bool) {\n    return account == controller;\n  }\n}\n"
      },
      "contracts/base/interface/convex/IBaseRewardPool.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface IBaseRewardPool {\n    function balanceOf(address account) external view returns(uint256 amount);\n    function pid() external view returns (uint256 _pid);\n    function stakingToken() external view returns (address _stakingToken);\n    function getReward() external;\n    function stake(uint256 _amount) external;\n    function stakeAll() external;\n    function withdraw(uint256 amount, bool claim) external;\n    function withdrawAll(bool claim) external;\n    function withdrawAndUnwrap(uint256 amount, bool claim) external;\n    function withdrawAllAndUnwrap(bool claim) external;\n}\n"
      },
      "contracts/base/interface/convex/IBooster.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface IBooster {\n    function deposit(uint256 _pid, uint256 _amount, bool _stake) external;\n    function depositAll(uint256 _pid, bool _stake) external;\n    function withdraw(uint256 _pid, uint256 _amount) external;\n    function withdrawAll(uint256 _pid) external;\n    function poolInfo(uint256 _pid) external view returns (address lpToken, address, address, address, address, bool);\n    function earmarkRewards(uint256 _pid) external;\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_2token_new.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_2token_new {\n  function get_virtual_price() external view returns (uint);\n  function add_liquidity(\n    uint256[2] calldata amounts,\n    uint256 min_mint_amount,\n    bool use_eth\n  ) external payable;\n  function remove_liquidity_imbalance(\n    uint256[2] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    uint256 _amount,\n    uint256[2] calldata amounts\n  ) external;\n  function exchange(\n    int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount\n  ) external payable;\n  function calc_token_amount(\n    uint256[2] calldata amounts,\n    bool deposit\n  ) external view returns(uint);\n  function get_dy(\n    int128 i,\n    int128 j,\n    uint256 dx\n  ) external view returns(uint256);\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_2token.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_2token {\n  function get_virtual_price() external view returns (uint);\n  function add_liquidity(\n    uint256[2] calldata amounts,\n    uint256 min_mint_amount\n  ) external payable;\n  function remove_liquidity_imbalance(\n    uint256[2] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    uint256 _amount,\n    uint256[2] calldata amounts\n  ) external;\n  function exchange(\n    int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount\n  ) external payable;\n  function calc_token_amount(\n    uint256[2] calldata amounts,\n    bool deposit\n  ) external view returns(uint);\n  function get_dy(\n    int128 i,\n    int128 j,\n    uint256 dx\n  ) external view returns(uint256);\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_3token_meta.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_3token_meta {\n  function add_liquidity(\n    address pool,\n    uint256[3] calldata amounts,\n    uint256 min_mint_amount\n  ) external;\n  function remove_liquidity_imbalance(\n    address pool,\n    uint256[3] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    address pool,\n    uint256 _amount,\n    uint256[3] calldata amounts\n  ) external;\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_3token.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_3token {\n  function get_virtual_price() external view returns (uint);\n  function add_liquidity(\n    uint256[3] calldata amounts,\n    uint256 min_mint_amount\n  ) external;\n  function remove_liquidity_imbalance(\n    uint256[3] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    uint256 _amount,\n    uint256[3] calldata amounts\n  ) external;\n  function exchange(\n    int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount\n  ) external;\n  function calc_token_amount(\n    uint256[3] calldata amounts,\n    bool deposit\n  ) external view returns(uint);\n  function calc_withdraw_one_coin(\n    uint256 _token_amount,\n    int128 i\n  ) external view returns(uint256);\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_4token_meta.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_4token_meta {\n  function add_liquidity(\n    address pool,\n    uint256[4] calldata amounts,\n    uint256 min_mint_amount\n  ) external;\n  function remove_liquidity_imbalance(\n    address pool,\n    uint256[4] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    address pool,\n    uint256 _amount,\n    uint256[4] calldata amounts\n  ) external;\n}\n"
      },
      "contracts/base/interface/curve/ICurveDeposit_4token.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface ICurveDeposit_4token {\n  function get_virtual_price() external view returns (uint);\n  function add_liquidity(\n    uint256[4] calldata amounts,\n    uint256 min_mint_amount\n  ) external;\n  function remove_liquidity_imbalance(\n    uint256[4] calldata amounts,\n    uint256 max_burn_amount\n  ) external;\n  function remove_liquidity(\n    uint256 _amount,\n    uint256[4] calldata amounts\n  ) external;\n  function exchange(\n    int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount\n  ) external;\n  function calc_token_amount(\n    uint256[4] calldata amounts,\n    bool deposit\n  ) external view returns(uint);\n}\n"
      },
      "contracts/base/interface/IController.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\n\ninterface IController {\n\n    // ========================= Events =========================\n\n    event QueueProfitSharingChange(uint profitSharingNumerator, uint validAtTimestamp);\n    event ConfirmProfitSharingChange(uint profitSharingNumerator);\n\n    event QueueStrategistFeeChange(uint strategistFeeNumerator, uint validAtTimestamp);\n    event ConfirmStrategistFeeChange(uint strategistFeeNumerator);\n\n    event QueuePlatformFeeChange(uint platformFeeNumerator, uint validAtTimestamp);\n    event ConfirmPlatformFeeChange(uint platformFeeNumerator);\n\n    event QueueNextImplementationDelay(uint implementationDelay, uint validAtTimestamp);\n    event ConfirmNextImplementationDelay(uint implementationDelay);\n\n    event AddedStakingContract(address indexed stakingContract);\n    event RemovedStakingContract(address indexed stakingContract);\n\n    event SharePriceChangeLog(\n        address indexed vault,\n        address indexed strategy,\n        uint256 oldSharePrice,\n        uint256 newSharePrice,\n        uint256 timestamp\n    );\n\n    // ==================== Functions ====================\n\n    /**\n     * An EOA can safely interact with the system no matter what. If you're using Metamask, you're using an EOA. Only\n     * smart contracts may be affected by this grey list. This contract will not be able to ban any EOA from the system\n     * even if an EOA is being added to the greyList, he/she will still be able to interact with the whole system as if\n     * nothing happened. Only smart contracts will be affected by being added to the greyList. This grey list is only\n     * used in VaultV3.sol, see the code there for reference\n     */\n    function greyList(address _target) external view returns (bool);\n\n    function addressWhiteList(address _target) external view returns (bool);\n\n    function codeWhiteList(address _target) external view returns (bool);\n\n    function addToWhitelist(address _target) external;\n\n    function addCodeToWhitelist(address _target) external;\n\n    function store() external view returns (address);\n\n    function governance() external view returns (address);\n\n    function doHardWork(address _vault) external;\n\n    function addHardWorker(address _worker) external;\n\n    function removeHardWorker(address _worker) external;\n\n    function salvage(address _token, uint256 amount) external;\n\n    function salvageStrategy(address _strategy, address _token, uint256 amount) external;\n\n    /**\n     * @return The targeted profit token to convert all-non-compounding rewards to. Defaults to WETH.\n     */\n    function targetToken() external view returns (address);\n\n    function setTargetToken(address _targetToken) external;\n\n    function profitSharingReceiver() external view returns (address);\n\n    function setProfitSharingReceiver(address _profitSharingReceiver) external;\n\n    function protocolFeeReceiver() external view returns (address);\n\n    function setProtocolFeeReceiver(address _protocolFeeReceiver) external;\n\n    function rewardForwarder() external view returns (address);\n\n    function setRewardForwarder(address _rewardForwarder) external;\n\n    function universalLiquidator() external view returns (address);\n\n    function setUniversalLiquidator(address _universalLiquidator) external;\n\n    function dolomiteYieldFarmingRouter() external view returns (address);\n\n    function setDolomiteYieldFarmingRouter(address _value) external;\n\n    function nextImplementationDelay() external view returns (uint256);\n\n    function profitSharingNumerator() external view returns (uint256);\n\n    function strategistFeeNumerator() external view returns (uint256);\n\n    function platformFeeNumerator() external view returns (uint256);\n\n    function feeDenominator() external view returns (uint256);\n\n    function setProfitSharingNumerator(uint _profitSharingNumerator) external;\n\n    function confirmSetProfitSharingNumerator() external;\n\n    function setStrategistFeeNumerator(uint _strategistFeeNumerator) external;\n\n    function confirmSetStrategistFeeNumerator() external;\n\n    function setPlatformFeeNumerator(uint _platformFeeNumerator) external;\n\n    function confirmSetPlatformFeeNumerator() external;\n\n    function setNextImplementationDelay(uint256 _nextImplementationDelay) external;\n\n    function confirmNextImplementationDelay() external;\n\n    function nextProfitSharingNumerator() external view returns (uint256);\n\n    function nextProfitSharingNumeratorTimestamp() external view returns (uint256);\n\n    function nextStrategistFeeNumerator() external view returns (uint256);\n\n    function nextStrategistFeeNumeratorTimestamp() external view returns (uint256);\n\n    function nextPlatformFeeNumerator() external view returns (uint256);\n\n    function nextPlatformFeeNumeratorTimestamp() external view returns (uint256);\n\n    function tempNextImplementationDelay() external view returns (uint256);\n\n    function tempNextImplementationDelayTimestamp() external view returns (uint256);\n}\n"
      },
      "contracts/base/interface/IRewardForwarder.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\n\n/**\n * @dev A routing contract that is responsible for taking the harvested gains and routing them into FARM and additional\n *      buyback tokens for the corresponding strategy\n */\ninterface IRewardForwarder {\n\n    function store() external view returns (address);\n\n    function governance() external view returns (address);\n\n    /**\n     * @dev This function sends converted `_buybackTokens` to `msg.sender`. The returned amounts will match the\n     *      `amounts` return value. The fee amounts are converted to the profit sharing token and sent to the proper\n     *      addresses (profit sharing, strategist, and governance (platform)).\n     *\n     * @param _token            the token that will be compounded or sold into the profit sharing token for the Harvest\n     *                          collective (users that stake iFARM)\n     * @param _profitSharingFee the amount of `_token` that will be sold into the profit sharing token\n     * @param _strategistFee    the amount of `_token` that will be sold into the profit sharing token for the\n     *                          strategist\n     * @param _platformFee      the amount of `_token` that will be sold into the profit sharing token for the Harvest\n     *                          treasury\n     * @param _buybackTokens    the output tokens that `_buyBackAmounts` should be swapped to (outputToken)\n     * @param _buybackAmounts   the amounts of `_token` that will be bought into more `_buybackTokens` token\n     * @return amounts The amounts that were purchased of _buybackTokens\n     */\n    function notifyFeeAndBuybackAmounts(\n        address _token,\n        uint256 _profitSharingFee,\n        uint256 _strategistFee,\n        uint256 _platformFee,\n        address[] calldata _buybackTokens,\n        uint256[] calldata _buybackAmounts\n    ) external returns (uint[] memory amounts);\n\n    /**\n     * @dev This function converts the fee amounts to the profit sharing token and sends them to the proper addresses\n     *      (profit sharing, strategist, and governance (platform)).\n     *\n     * @param _token            the token that will be compounded or sold into the profit sharing token for the Harvest\n     *                          collective (users that stake iFARM)\n     * @param _profitSharingFee the amount of `_token` that will be sold into the profit sharing token\n     * @param _strategistFee    the amount of `_token` that will be sold into the profit sharing token for the\n     *                          strategist\n     * @param _platformFee      the amount of `_token` that will be sold into the profit sharing token for the Harvest\n     *                          treasury\n     */\n    function notifyFee(\n        address _token,\n        uint256 _profitSharingFee,\n        uint256 _strategistFee,\n        uint256 _platformFee\n    ) external;\n}\n"
      },
      "contracts/base/interface/IUniversalLiquidator.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.6.12;\r\n\r\ninterface IUniversalLiquidator {\r\n    event Swap(\r\n        address indexed sellToken,\r\n        address indexed buyToken,\r\n        address indexed receiver,\r\n        address initiator,\r\n        uint256 sellAmount,\r\n        uint256 minBuyAmount\r\n    );\r\n\r\n    function swap(\r\n        address _sellToken,\r\n        address _buyToken,\r\n        uint256 _sellAmount,\r\n        uint256 _minBuyAmount,\r\n        address _receiver\r\n    ) external returns (uint256);\r\n}\r\n"
      },
      "contracts/base/interface/weth/IWETH.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\ninterface IWETH {\n    function deposit() external payable;\n    function transfer(address to, uint value) external returns (bool);\n    function withdraw(uint) external;\n}\n"
      },
      "contracts/base/upgradability/BaseUpgradeableStrategy.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"./BaseUpgradeableStrategyStorage.sol\";\nimport \"../inheritance/ControllableInit.sol\";\nimport \"../interface/IController.sol\";\nimport \"../interface/IRewardForwarder.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\n\ncontract BaseUpgradeableStrategy is Initializable, ControllableInit, BaseUpgradeableStrategyStorage {\n  using SafeMath for uint256;\n  using SafeERC20 for IERC20;\n\n  event ProfitsNotCollected(bool sell, bool floor);\n  event ProfitLogInReward(uint256 profitAmount, uint256 feeAmount, uint256 timestamp);\n  event ProfitAndBuybackLog(uint256 profitAmount, uint256 feeAmount, uint256 timestamp);\n\n  modifier restricted() {\n    require(msg.sender == vault() || msg.sender == controller()\n      || msg.sender == governance(),\n      \"The sender has to be the controller, governance, or vault\");\n    _;\n  }\n\n  // This is only used in `investAllUnderlying()`\n  // The user can still freely withdraw from the strategy\n  modifier onlyNotPausedInvesting() {\n    require(!pausedInvesting(), \"Action blocked as the strategy is in emergency state\");\n    _;\n  }\n\n  constructor() public BaseUpgradeableStrategyStorage() {\n  }\n\n  function initialize(\n    address _storage,\n    address _underlying,\n    address _vault,\n    address _rewardPool,\n    address _rewardToken,\n    address _strategist\n  ) public initializer {\n    ControllableInit.initialize(\n      _storage\n    );\n    _setUnderlying(_underlying);\n    _setVault(_vault);\n    _setRewardPool(_rewardPool);\n    _setRewardToken(_rewardToken);\n    _setStrategist(_strategist);\n    _setSell(true);\n    _setSellFloor(0);\n    _setPausedInvesting(false);\n  }\n\n  /**\n  * Schedules an upgrade for this vault's proxy.\n  */\n  function scheduleUpgrade(address impl) public onlyGovernance {\n    _setNextImplementation(impl);\n    _setNextImplementationTimestamp(block.timestamp.add(nextImplementationDelay()));\n  }\n\n  function _finalizeUpgrade() internal {\n    _setNextImplementation(address(0));\n    _setNextImplementationTimestamp(0);\n  }\n\n  function shouldUpgrade() external view returns (bool, address) {\n    return (\n      nextImplementationTimestamp() != 0\n        && block.timestamp > nextImplementationTimestamp()\n        && nextImplementation() != address(0),\n      nextImplementation()\n    );\n  }\n\n  // ========================= Internal & Private Functions =========================\n\n  // ==================== Functionality ====================\n\n  /**\n    * @dev Same as `_notifyProfitAndBuybackInRewardToken` but does not perform a compounding buyback. Just takes fees\n    *      instead.\n    */\n  function _notifyProfitInRewardToken(\n      address _rewardToken,\n      uint256 _rewardBalance\n  ) internal {\n      if (_rewardBalance > 100) {\n          uint _feeDenominator = feeDenominator();\n          uint256 strategistFee = _rewardBalance.mul(strategistFeeNumerator()).div(_feeDenominator);\n          uint256 platformFee = _rewardBalance.mul(platformFeeNumerator()).div(_feeDenominator);\n          uint256 profitSharingFee = _rewardBalance.mul(profitSharingNumerator()).div(_feeDenominator);\n\n          address strategyFeeRecipient = strategist();\n          address platformFeeRecipient = IController(controller()).governance();\n\n          emit ProfitLogInReward(\n              _rewardToken,\n              _rewardBalance,\n              profitSharingFee,\n              block.timestamp\n          );\n          emit PlatformFeeLogInReward(\n              platformFeeRecipient,\n              _rewardToken,\n              _rewardBalance,\n              platformFee,\n              block.timestamp\n          );\n          emit StrategistFeeLogInReward(\n              strategyFeeRecipient,\n              _rewardToken,\n              _rewardBalance,\n              strategistFee,\n              block.timestamp\n          );\n\n          address rewardForwarder = IController(controller()).rewardForwarder();\n          IERC20(_rewardToken).safeApprove(rewardForwarder, 0);\n          IERC20(_rewardToken).safeApprove(rewardForwarder, _rewardBalance);\n\n          // Distribute/send the fees\n          IRewardForwarder(rewardForwarder).notifyFee(\n              _rewardToken,\n              profitSharingFee,\n              strategistFee,\n              platformFee\n          );\n      } else {\n          emit ProfitLogInReward(_rewardToken, 0, 0, block.timestamp);\n          emit PlatformFeeLogInReward(IController(controller()).governance(), _rewardToken, 0, 0, block.timestamp);\n          emit StrategistFeeLogInReward(strategist(), _rewardToken, 0, 0, block.timestamp);\n      }\n  }\n}\n"
      },
      "contracts/base/upgradability/BaseUpgradeableStrategyStorage.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"../interface/IController.sol\";\nimport \"../inheritance/ControllableInit.sol\";\n\ncontract BaseUpgradeableStrategyStorage is ControllableInit {\n\n  event ProfitsNotCollected(\n      address indexed rewardToken,\n      bool sell,\n      bool floor\n  );\n  event ProfitLogInReward(\n      address indexed rewardToken,\n      uint256 profitAmount,\n      uint256 feeAmount,\n      uint256 timestamp\n  );\n  event ProfitAndBuybackLog(\n      address indexed rewardToken,\n      uint256 profitAmount,\n      uint256 feeAmount,\n      uint256 timestamp\n  );\n  event PlatformFeeLogInReward(\n      address indexed treasury,\n      address indexed rewardToken,\n      uint256 profitAmount,\n      uint256 feeAmount,\n      uint256 timestamp\n  );\n  event StrategistFeeLogInReward(\n      address indexed strategist,\n      address indexed rewardToken,\n      uint256 profitAmount,\n      uint256 feeAmount,\n      uint256 timestamp\n  );\n\n  bytes32 internal constant _UNDERLYING_SLOT = 0xa1709211eeccf8f4ad5b6700d52a1a9525b5f5ae1e9e5f9e5a0c2fc23c86e530;\n  bytes32 internal constant _VAULT_SLOT = 0xefd7c7d9ef1040fc87e7ad11fe15f86e1d11e1df03c6d7c87f7e1f4041f08d41;\n\n  bytes32 internal constant _REWARD_TOKEN_SLOT = 0xdae0aafd977983cb1e78d8f638900ff361dc3c48c43118ca1dd77d1af3f47bbf;\n  bytes32 internal constant _REWARD_TOKENS_SLOT = 0x45418d9b5c2787ae64acbffccad43f2b487c1a16e24385aa9d2b059f9d1d163c;\n  bytes32 internal constant _REWARD_POOL_SLOT = 0x3d9bb16e77837e25cada0cf894835418b38e8e18fbec6cfd192eb344bebfa6b8;\n  bytes32 internal constant _SELL_FLOOR_SLOT = 0xc403216a7704d160f6a3b5c3b149a1226a6080f0a5dd27b27d9ba9c022fa0afc;\n  bytes32 internal constant _SELL_SLOT = 0x656de32df98753b07482576beb0d00a6b949ebf84c066c765f54f26725221bb6;\n  bytes32 internal constant _PAUSED_INVESTING_SLOT = 0xa07a20a2d463a602c2b891eb35f244624d9068572811f63d0e094072fb54591a;\n\n  bytes32 internal constant _PROFIT_SHARING_NUMERATOR_SLOT = 0xe3ee74fb7893020b457d8071ed1ef76ace2bf4903abd7b24d3ce312e9c72c029;\n  bytes32 internal constant _PROFIT_SHARING_DENOMINATOR_SLOT = 0x0286fd414602b432a8c80a0125e9a25de9bba96da9d5068c832ff73f09208a3b;\n\n  bytes32 internal constant _NEXT_IMPLEMENTATION_SLOT = 0x29f7fcd4fe2517c1963807a1ec27b0e45e67c60a874d5eeac7a0b1ab1bb84447;\n  bytes32 internal constant _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT = 0x414c5263b05428f1be1bfa98e25407cc78dd031d0d3cd2a2e3d63b488804f22e;\n  bytes32 internal constant _NEXT_IMPLEMENTATION_DELAY_SLOT = 0x82b330ca72bcd6db11a26f10ce47ebcfe574a9c646bccbc6f1cd4478eae16b31;\n\n  bytes32 internal constant _STRATEGIST_SLOT = 0x6a7b588c950d46e2de3db2f157e5e0e4f29054c8d60f17bf0c30352e223a458d;\n\n  constructor() public {\n    assert(_UNDERLYING_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.underlying\")) - 1));\n    assert(_VAULT_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.vault\")) - 1));\n    assert(_REWARD_TOKEN_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.rewardToken\")) - 1));\n    assert(_REWARD_TOKENS_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.rewardTokens\")) - 1));\n    assert(_REWARD_POOL_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.rewardPool\")) - 1));\n    assert(_SELL_FLOOR_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.sellFloor\")) - 1));\n    assert(_SELL_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.sell\")) - 1));\n    assert(_PAUSED_INVESTING_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.pausedInvesting\")) - 1));\n\n    assert(_PROFIT_SHARING_NUMERATOR_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.profitSharingNumerator\")) - 1));\n    assert(_PROFIT_SHARING_DENOMINATOR_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.profitSharingDenominator\")) - 1));\n\n    assert(_NEXT_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.nextImplementation\")) - 1));\n    assert(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.nextImplementationTimestamp\")) - 1));\n    assert(_NEXT_IMPLEMENTATION_DELAY_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.nextImplementationDelay\")) - 1));\n\n    assert(_STRATEGIST_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.strategist\")) - 1));\n  }\n\n  function _setUnderlying(address _address) internal {\n    setAddress(_UNDERLYING_SLOT, _address);\n  }\n\n  function underlying() public virtual view returns (address) {\n    return getAddress(_UNDERLYING_SLOT);\n  }\n\n  function _setRewardPool(address _address) internal {\n    setAddress(_REWARD_POOL_SLOT, _address);\n  }\n\n  function rewardPool() public view returns (address) {\n    return getAddress(_REWARD_POOL_SLOT);\n  }\n\n  function _setRewardToken(address _address) internal {\n    setAddress(_REWARD_TOKEN_SLOT, _address);\n  }\n\n  function rewardToken() public view returns (address) {\n    return getAddress(_REWARD_TOKEN_SLOT);\n  }\n\n  function _setRewardTokens(address[] memory _rewardTokens) internal {\n    setAddressArray(_REWARD_TOKENS_SLOT, _rewardTokens);\n  }\n\n  function isRewardToken(address _token) public view returns (bool) {\n    return _isAddressInList(_token, rewardTokens());\n  }\n\n  function rewardTokens() public view returns (address[] memory) {\n    return getAddressArray(_REWARD_TOKENS_SLOT);\n  }\n\n  function _isAddressInList(address _searchValue, address[] memory _list) internal pure returns (bool) {\n    for (uint i = 0; i < _list.length; i++) {\n      if (_list[i] == _searchValue) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  function _setStrategist(address _strategist) internal {\n    setAddress(_STRATEGIST_SLOT, _strategist);\n  }\n\n  function strategist() public view returns (address) {\n    return getAddress(_STRATEGIST_SLOT);\n  }\n\n  function _setVault(address _address) internal {\n    setAddress(_VAULT_SLOT, _address);\n  }\n\n  function vault() public virtual view returns (address) {\n    return getAddress(_VAULT_SLOT);\n  }\n\n  // a flag for disabling selling for simplified emergency exit\n  function _setSell(bool _value) internal {\n    setBoolean(_SELL_SLOT, _value);\n  }\n\n  function sell() public view returns (bool) {\n    return getBoolean(_SELL_SLOT);\n  }\n\n  function _setPausedInvesting(bool _value) internal {\n    setBoolean(_PAUSED_INVESTING_SLOT, _value);\n  }\n\n  function pausedInvesting() public view returns (bool) {\n    return getBoolean(_PAUSED_INVESTING_SLOT);\n  }\n\n  function _setSellFloor(uint256 _value) internal {\n    setUint256(_SELL_FLOOR_SLOT, _value);\n  }\n\n  function sellFloor() public view returns (uint256) {\n    return getUint256(_SELL_FLOOR_SLOT);\n  }\n\n  function profitSharingNumerator() public view returns (uint256) {\n    return IController(controller()).profitSharingNumerator();\n  }\n\n  function platformFeeNumerator() public view returns (uint256) {\n    return IController(controller()).platformFeeNumerator();\n  }\n\n  function strategistFeeNumerator() public view returns (uint256) {\n    return IController(controller()).strategistFeeNumerator();\n  }\n\n  function feeDenominator() public view returns (uint256) {\n    return IController(controller()).feeDenominator();\n  }\n\n  function universalLiquidator() public view returns (address) {\n    return IController(controller()).universalLiquidator();\n  }\n\n  // upgradeability\n\n  function _setNextImplementation(address _address) internal {\n    setAddress(_NEXT_IMPLEMENTATION_SLOT, _address);\n  }\n\n  function nextImplementation() public view returns (address) {\n    return getAddress(_NEXT_IMPLEMENTATION_SLOT);\n  }\n\n  function _setNextImplementationTimestamp(uint256 _value) internal {\n    setUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT, _value);\n  }\n\n  function nextImplementationTimestamp() public view returns (uint256) {\n    return getUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT);\n  }\n\n    function nextImplementationDelay() public view returns (uint256) {\n        return IController(controller()).nextImplementationDelay();\n    }\n\n  function setBoolean(bytes32 slot, bool _value) internal {\n    setUint256(slot, _value ? 1 : 0);\n  }\n\n  function getBoolean(bytes32 slot) internal view returns (bool) {\n    return (getUint256(slot) == 1);\n  }\n\n  function setAddress(bytes32 slot, address _address) internal {\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      sstore(slot, _address)\n    }\n  }\n\n  function setUint256(bytes32 slot, uint256 _value) internal {\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      sstore(slot, _value)\n    }\n  }\n\n  function getAddress(bytes32 slot) internal view returns (address str) {\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      str := sload(slot)\n    }\n  }\n\n  function getUint256(bytes32 slot) internal view returns (uint256 str) {\n    // solhint-disable-next-line no-inline-assembly\n    assembly {\n      str := sload(slot)\n    }\n  }\n\n      function setUint256Array(bytes32 slot, uint256[] memory _values) internal {\n        // solhint-disable-next-line no-inline-assembly\n        setUint256(slot, _values.length);\n        for (uint i = 0; i < _values.length; i++) {\n            setUint256(bytes32(uint(slot) + 1 + i), _values[i]);\n        }\n    }\n\n    function setAddressArray(bytes32 slot, address[] memory _values) internal {\n        // solhint-disable-next-line no-inline-assembly\n        setUint256(slot, _values.length);\n        for (uint i = 0; i < _values.length; i++) {\n            setAddress(bytes32(uint(slot) + 1 + i), _values[i]);\n        }\n    }\n\n\n    function getUint256Array(bytes32 slot) internal view returns (uint[] memory values) {\n        // solhint-disable-next-line no-inline-assembly\n        values = new uint[](getUint256(slot));\n        for (uint i = 0; i < values.length; i++) {\n            values[i] = getUint256(bytes32(uint(slot) + 1 + i));\n        }\n    }\n\n    function getAddressArray(bytes32 slot) internal view returns (address[] memory values) {\n        // solhint-disable-next-line no-inline-assembly\n        values = new address[](getUint256(slot));\n        for (uint i = 0; i < values.length; i++) {\n            values[i] = getAddress(bytes32(uint(slot) + 1 + i));\n        }\n    }\n\n    function setBytes32(bytes32 slot, bytes32 _value) internal {\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n        sstore(slot, _value)\n        }\n    }\n\n    function getBytes32(bytes32 slot) internal view returns (bytes32 str) {\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n        str := sload(slot)\n        }\n    }\n}\n"
      },
      "contracts/base/upgradability/ReentrancyGuardUpgradeable.sol": {
        "content": "//SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\n\n/**\n * Same old `ReentrancyGuard`, but can be used by upgradable contracts\n */\ncontract ReentrancyGuardUpgradeable is Initializable {\n\n    bytes32 internal constant _NOT_ENTERED_SLOT = 0x62ae7bf2df4e95c187ea09c8c47c3fc3d9abc36298f5b5b6c5e2e7b4b291fe25;\n\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_getNotEntered(_NOT_ENTERED_SLOT), \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _setNotEntered(_NOT_ENTERED_SLOT, false);\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _setNotEntered(_NOT_ENTERED_SLOT, true);\n    }\n\n    constructor() public {\n        assert(_NOT_ENTERED_SLOT == bytes32(uint256(keccak256(\"eip1967.reentrancyGuard.notEntered\")) - 1));\n    }\n\n    function initialize() public initializer {\n        _setNotEntered(_NOT_ENTERED_SLOT, true);\n    }\n\n    function _getNotEntered(bytes32 slot) private view returns (bool) {\n        uint str;\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            str := sload(slot)\n        }\n        return str == 1;\n    }\n\n    function _setNotEntered(bytes32 slot, bool _value) private {\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            sstore(slot, _value)\n        }\n    }\n\n}\n"
      },
      "contracts/strategies/convex/ConvexStrategy.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts/math/Math.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"../../base/upgradability/BaseUpgradeableStrategy.sol\";\nimport \"../../base/interface/IUniversalLiquidator.sol\";\nimport \"../../base/interface/convex/IBooster.sol\";\nimport \"../../base/interface/convex/IBaseRewardPool.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_2token.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_2token_new.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_3token.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_3token_meta.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_4token.sol\";\nimport \"../../base/interface/curve/ICurveDeposit_4token_meta.sol\";\nimport \"../../base/interface/weth/IWETH.sol\";\n\ncontract ConvexStrategy is BaseUpgradeableStrategy {\n\n  using SafeMath for uint256;\n  using SafeERC20 for IERC20;\n\n  address public constant booster = address(0xF403C135812408BFbE8713b5A23a04b3D48AAE31);\n  address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n  address public constant multiSigAddr = address(0xF49440C1F012d041802b25A73e5B0B9166a75c02);\n\n  // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here\n  bytes32 internal constant _POOLID_SLOT = 0x3fd729bfa2e28b7806b03a6e014729f59477b530f995be4d51defc9dad94810b;\n  bytes32 internal constant _DEPOSIT_TOKEN_SLOT = 0x219270253dbc530471c88a9e7c321b36afda219583431e7b6c386d2d46e70c86;\n  bytes32 internal constant _DEPOSIT_ARRAY_POSITION_SLOT = 0xb7c50ef998211fff3420379d0bf5b8dfb0cee909d1b7d9e517f311c104675b09;\n  bytes32 internal constant _CURVE_DEPOSIT_SLOT = 0xb306bb7adebd5a22f5e4cdf1efa00bc5f62d4f5554ef9d62c1b16327cd3ab5f9;\n  bytes32 internal constant _NTOKENS_SLOT = 0xbb60b35bae256d3c1378ff05e8d7bee588cd800739c720a107471dfa218f74c1;\n  bytes32 internal constant _METAPOOL_SLOT = 0x567ad8b67c826974a167f1a361acbef5639a3e7e02e99edbc648a84b0923d5b7;\n\n  address[] public rewardTokens;\n\n  constructor() public BaseUpgradeableStrategy() {\n    assert(_POOLID_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.poolId\")) - 1));\n    assert(_DEPOSIT_TOKEN_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.depositToken\")) - 1));\n    assert(_DEPOSIT_ARRAY_POSITION_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.depositArrayPosition\")) - 1));\n    assert(_CURVE_DEPOSIT_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.curveDeposit\")) - 1));\n    assert(_NTOKENS_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.nTokens\")) - 1));\n    assert(_METAPOOL_SLOT == bytes32(uint256(keccak256(\"eip1967.strategyStorage.metaPool\")) - 1));\n  }\n\n  function initializeBaseStrategy(\n    address _storage,\n    address _underlying,\n    address _vault,\n    address _rewardPool,\n    uint256 _poolID,\n    address _depositToken,\n    uint256 _depositArrayPosition,\n    address _curveDeposit,\n    uint256 _nTokens,\n    bool _metaPool\n  ) public initializer {\n\n    BaseUpgradeableStrategy.initialize(\n      _storage,\n      _underlying,\n      _vault,\n      _rewardPool,\n      weth,\n      multiSigAddr\n    );\n\n    address _lpt;\n    (_lpt,,,,,) = IBooster(booster).poolInfo(_poolID);\n    require(_lpt == underlying(), \"Pool Info does not match underlying\");\n    require(_depositArrayPosition < _nTokens, \"Deposit array position out of bounds\");\n    require(1 < _nTokens && _nTokens < 5, \"_nTokens should be 2, 3 or 4\");\n    _setDepositArrayPosition(_depositArrayPosition);\n    _setPoolId(_poolID);\n    _setDepositToken(_depositToken);\n    _setCurveDeposit(_curveDeposit);\n    _setNTokens(_nTokens);\n    _setMetaPool(_metaPool);\n  }\n\n  function depositArbCheck() public pure returns(bool) {\n    return true;\n  }\n\n  function rewardPoolBalance() internal view returns (uint256 bal) {\n      bal = IBaseRewardPool(rewardPool()).balanceOf(address(this));\n  }\n\n  function exitRewardPool() internal {\n      uint256 stakedBalance = rewardPoolBalance();\n      if (stakedBalance != 0) {\n          IBaseRewardPool(rewardPool()).withdrawAllAndUnwrap(true);\n      }\n  }\n\n  function partialWithdrawalRewardPool(uint256 amount) internal {\n    IBaseRewardPool(rewardPool()).withdrawAndUnwrap(amount, false);  //don't claim rewards at this point\n  }\n\n  function emergencyExitRewardPool() internal {\n    uint256 stakedBalance = rewardPoolBalance();\n    if (stakedBalance != 0) {\n        IBaseRewardPool(rewardPool()).withdrawAllAndUnwrap(false); //don't claim rewards\n    }\n  }\n\n  function unsalvagableTokens(address token) public view returns (bool) {\n    return (token == rewardToken() || token == underlying());\n  }\n\n  function enterRewardPool() internal {\n    address _underlying = underlying();\n    uint256 entireBalance = IERC20(_underlying).balanceOf(address(this));\n    IERC20(_underlying).safeApprove(booster, 0);\n    IERC20(_underlying).safeApprove(booster, entireBalance);\n    IBooster(booster).depositAll(poolId(), true); //deposit and stake\n  }\n\n  /*\n  *   In case there are some issues discovered about the pool or underlying asset\n  *   Governance can exit the pool properly\n  *   The function is only used for emergency to exit the pool\n  */\n  function emergencyExit() public onlyGovernance {\n    emergencyExitRewardPool();\n    _setPausedInvesting(true);\n  }\n\n  /*\n  *   Resumes the ability to invest into the underlying reward pools\n  */\n\n  function continueInvesting() public onlyGovernance {\n    _setPausedInvesting(false);\n  }\n\n  function addRewardToken(address _token) public onlyGovernance {\n    rewardTokens.push(_token);\n  }\n\n  // We assume that all the tradings can be done on Sushiswap\n  function _liquidateReward() internal {\n    if (!sell()) {\n      // Profits can be disabled for possible simplified and rapoolId exit\n      emit ProfitsNotCollected(sell(), false);\n      return;\n    }\n\n    address _rewardToken = rewardToken();\n    address _universalLiquidator = universalLiquidator();\n    address _depositToken = depositToken();\n\n    for(uint256 i = 0; i < rewardTokens.length; i++){\n      address token = rewardTokens[i];\n      uint256 rewardBalance = IERC20(token).balanceOf(address(this));\n\n      if (rewardBalance == 0 || token == _rewardToken) {\n        continue;\n      }\n\n      IERC20(token).safeApprove(_universalLiquidator, 0);\n      IERC20(token).safeApprove(_universalLiquidator, rewardBalance);\n      IUniversalLiquidator(_universalLiquidator).swap(token, _rewardToken, rewardBalance, 1, address(this));\n    }\n\n    uint256 rewardBalance = IERC20(_rewardToken).balanceOf(address(this));\n    _notifyProfitInRewardToken(_rewardToken, rewardBalance);\n    uint256 remainingRewardBalance = IERC20(_rewardToken).balanceOf(address(this));\n\n    if (remainingRewardBalance == 0) {\n      return;\n    }\n\n    if(_depositToken != _rewardToken) {\n      IERC20(_rewardToken).safeApprove(_universalLiquidator, 0);\n      IERC20(_rewardToken).safeApprove(_universalLiquidator, remainingRewardBalance);\n      IUniversalLiquidator(_universalLiquidator).swap(_rewardToken, _depositToken, remainingRewardBalance, 1, address(this));\n    }\n\n    uint256 tokenBalance = IERC20(_depositToken).balanceOf(address(this));\n    if (tokenBalance > 0) {\n      depositCurve();\n    }\n  }\n\n  function depositCurve() internal {\n    address _depositToken = depositToken();\n    address _curveDeposit = curveDeposit();\n\n    uint256 tokenBalance = IERC20(_depositToken).balanceOf(address(this));\n    if (_depositToken != weth) {\n      IERC20(_depositToken).safeApprove(_curveDeposit, 0);\n      IERC20(_depositToken).safeApprove(_curveDeposit, tokenBalance);\n    }\n\n    // we can accept 0 as minimum, this will be called only by trusted roles\n    uint256 minimum = 0;\n    if (nTokens() == 2) {\n      uint256[2] memory depositArray;\n      depositArray[depositArrayPosition()] = tokenBalance;\n      if (_depositToken == weth){\n        IWETH(weth).withdraw(tokenBalance);\n        ICurveDeposit_2token(_curveDeposit).add_liquidity{value:tokenBalance}(depositArray, minimum);\n      } else {\n        ICurveDeposit_2token(_curveDeposit).add_liquidity(depositArray, minimum);\n      }\n    } else if (nTokens() == 3) {\n      uint256[3] memory depositArray;\n      depositArray[depositArrayPosition()] = tokenBalance;\n      if (metaPool()) {\n        ICurveDeposit_3token_meta(_curveDeposit).add_liquidity(underlying(), depositArray, minimum);\n      } else {\n        ICurveDeposit_3token(_curveDeposit).add_liquidity(depositArray, minimum);\n      }\n    } else if (nTokens() == 4) {\n      uint256[4] memory depositArray;\n      depositArray[depositArrayPosition()] = tokenBalance;\n      if (metaPool()) {\n        ICurveDeposit_4token_meta(_curveDeposit).add_liquidity(underlying(), depositArray, minimum);\n      } else {\n        ICurveDeposit_4token(_curveDeposit).add_liquidity(depositArray, minimum);\n      }\n    }\n  }\n\n\n  /*\n  *   Stakes everything the strategy holds into the reward pool\n  */\n  function investAllUnderlying() internal onlyNotPausedInvesting {\n    // this check is needed, because most of the SNX reward pools will revert if\n    // you try to stake(0).\n    if(IERC20(underlying()).balanceOf(address(this)) > 0) {\n      enterRewardPool();\n    }\n  }\n\n  /*\n  *   Withdraws all the asset to the vault\n  */\n  function withdrawAllToVault() public restricted {\n    address _underlying = underlying();\n    if (address(rewardPool()) != address(0)) {\n      exitRewardPool();\n    }\n    _liquidateReward();\n    IERC20(_underlying).safeTransfer(vault(), IERC20(_underlying).balanceOf(address(this)));\n  }\n\n  /*\n  *   Withdraws all the asset to the vault\n  */\n  function withdrawToVault(uint256 amount) public restricted {\n    address _underlying = underlying();\n    // Typically there wouldn't be any amount here\n    // however, it is possible because of the emergencyExit\n    uint256 entireBalance = IERC20(_underlying).balanceOf(address(this));\n\n    if(amount > entireBalance){\n      // While we have the check above, we still using SafeMath below\n      // for the peace of mind (in case something gets changed in between)\n      uint256 needToWithdraw = amount.sub(entireBalance);\n      uint256 toWithdraw = Math.min(rewardPoolBalance(), needToWithdraw);\n      partialWithdrawalRewardPool(toWithdraw);\n    }\n    IERC20(_underlying).safeTransfer(vault(), amount);\n  }\n\n  /*\n  *   Note that we currently do not have a mechanism here to include the\n  *   amount of reward that is accrued.\n  */\n  function investedUnderlyingBalance() external view returns (uint256) {\n    return rewardPoolBalance()\n      .add(IERC20(underlying()).balanceOf(address(this)));\n  }\n\n  /*\n  *   Governance or Controller can claim coins that are somehow transferred into the contract\n  *   Note that they cannot come in take away coins that are used and defined in the strategy itself\n  */\n  function salvage(address recipient, address token, uint256 amount) external onlyControllerOrGovernance {\n     // To make sure that governance cannot come in and take away the coins\n    require(!unsalvagableTokens(token), \"token is defined as not salvagable\");\n    IERC20(token).safeTransfer(recipient, amount);\n  }\n\n  /*\n  *   Get the reward, sell it in exchange for underlying, invest what you got.\n  *   It's not much, but it's honest work.\n  *\n  *   Note that although `onlyNotPausedInvesting` is not added here,\n  *   calling `investAllUnderlying()` affectively blocks the usage of `doHardWork`\n  *   when the investing is being paused by governance.\n  */\n  function doHardWork() external onlyNotPausedInvesting restricted {\n    IBaseRewardPool(rewardPool()).getReward();\n    _liquidateReward();\n    investAllUnderlying();\n  }\n\n  /**\n  * Can completely disable claiming UNI rewards and selling. Good for emergency withdraw in the\n  * simplest possible way.\n  */\n  function setSell(bool s) public onlyGovernance {\n    _setSell(s);\n  }\n\n  /**\n  * Sets the minimum amount of CRV needed to trigger a sale.\n  */\n  function setSellFloor(uint256 floor) public onlyGovernance {\n    _setSellFloor(floor);\n  }\n\n  // masterchef rewards pool ID\n  function _setPoolId(uint256 _value) internal {\n    setUint256(_POOLID_SLOT, _value);\n  }\n\n  function poolId() public view returns (uint256) {\n    return getUint256(_POOLID_SLOT);\n  }\n\n  function _setDepositToken(address _address) internal {\n    setAddress(_DEPOSIT_TOKEN_SLOT, _address);\n  }\n\n  function depositToken() public view returns (address) {\n    return getAddress(_DEPOSIT_TOKEN_SLOT);\n  }\n\n  function _setDepositArrayPosition(uint256 _value) internal {\n    setUint256(_DEPOSIT_ARRAY_POSITION_SLOT, _value);\n  }\n\n  function depositArrayPosition() public view returns (uint256) {\n    return getUint256(_DEPOSIT_ARRAY_POSITION_SLOT);\n  }\n\n  function _setCurveDeposit(address _address) internal {\n    setAddress(_CURVE_DEPOSIT_SLOT, _address);\n  }\n\n  function curveDeposit() public view returns (address) {\n    return getAddress(_CURVE_DEPOSIT_SLOT);\n  }\n\n  function _setNTokens(uint256 _value) internal {\n    setUint256(_NTOKENS_SLOT, _value);\n  }\n\n  function nTokens() public view returns (uint256) {\n    return getUint256(_NTOKENS_SLOT);\n  }\n\n  function _setMetaPool(bool _value) internal {\n    setBoolean(_METAPOOL_SLOT, _value);\n  }\n\n  function metaPool() public view returns (bool) {\n    return getBoolean(_METAPOOL_SLOT);\n  }\n\n\n  function finalizeUpgrade() external onlyGovernance {\n    _finalizeUpgrade();\n  }\n\n  receive() external payable {}\n}\n"
      },
      "contracts/strategies/convex/ConvexStrategyMainnet_3CRV.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity 0.6.12;\n\nimport \"./ConvexStrategy.sol\";\n\ncontract ConvexStrategyMainnet_3CRV is ConvexStrategy {\n\n  constructor() public {}\n\n  function initializeStrategy(\n    address _storage,\n    address _vault\n  ) public initializer {\n    address underlying = address(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490); // Info -> LP Token address\n    address rewardPool = address(0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8); // Info -> Rewards contract address\n    address crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52);\n    address cvx = address(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B);\n    address usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);\n    address curveDeposit = address(0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7); // only needed if deposits are not via underlying\n    ConvexStrategy.initializeBaseStrategy(\n      _storage,\n      underlying,\n      _vault,\n      rewardPool, // rewardPool\n      9,  // Pool id: Info -> Rewards contract address -> read -> pid\n      usdc, // depositToken\n      1, //depositArrayPosition. Find deposit transaction -> input params\n      curveDeposit, // deposit contract: usually underlying. Find deposit transaction -> interacted contract\n      3, //nTokens -> total number of deposit tokens\n      false //metaPool -> if LP token address == pool address (at curve)\n    );\n    rewardTokens = [crv, cvx];\n  }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}