creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/access/AccessControl.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n    struct RoleData {\n        mapping(address account => bool) hasRole;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 role => RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with an {AccessControlUnauthorizedAccount} error including the required role.\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n        return _roles[role].hasRole[account];\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n     * is missing `role`.\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert AccessControlUnauthorizedAccount(account, role);\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n        if (callerConfirmation != _msgSender()) {\n            revert AccessControlBadConfirmation();\n        }\n\n        _revokeRole(role, callerConfirmation);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        _roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (!hasRole(role, account)) {\n            _roles[role].hasRole[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (hasRole(role, account)) {\n            _roles[role].hasRole[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/access/IAccessControl.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev The `account` is missing a role.\n     */\n    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n    /**\n     * @dev The caller of a function is not the expected one.\n     *\n     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n     */\n    error AccessControlBadConfirmation();\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\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    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) 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 a `value` amount of tokens as the allowance of `spender` over the\n     * 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 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` 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 from, address to, uint256 value) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC20Permit} from \"../extensions/IERC20Permit.sol\";\nimport {Address} from \"../../../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 Address for address;\n\n    /**\n     * @dev An operation with an ERC20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\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);\n        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {\n            revert SafeERC20FailedOperation(address(token));\n        }\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     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\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 cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev The ETH balance of the account is not enough to perform the operation.\n     */\n    error AddressInsufficientBalance(address account);\n\n    /**\n     * @dev There's no code at `target` (it is not a contract).\n     */\n    error AddressEmptyCode(address target);\n\n    /**\n     * @dev A call to an address target failed. The target may have reverted.\n     */\n    error FailedInnerCall();\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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        if (address(this).balance < amount) {\n            revert AddressInsufficientBalance(address(this));\n        }\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        if (!success) {\n            revert FailedInnerCall();\n        }\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 or custom error, it is bubbled\n     * up by this function (like regular Solidity function calls). However, if\n     * the call reverted with no returned reason, this function reverts with a\n     * {FailedInnerCall} error.\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    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0);\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    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        if (address(this).balance < value) {\n            revert AddressInsufficientBalance(address(this));\n        }\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an\n     * unsuccessful call.\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata\n    ) internal view returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            // only check if target is a contract if the call was successful and the return data is empty\n            // otherwise we already know that it was a contract\n            if (returndata.length == 0 && target.code.length == 0) {\n                revert AddressEmptyCode(target);\n            }\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n     * revert reason or with a default {FailedInnerCall} error.\n     */\n    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.\n     */\n    function _revert(bytes memory returndata) private pure {\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            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert FailedInnerCall();\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/introspection/ERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "contracts/interfaces/IController.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\ninterface IController {\n    function deposit(uint256[5] memory amounts, address receiver) external returns (uint256 shares);\n\n    function withdraw(uint256 shares, uint256[5] memory minTokenAmounts, address receiver) external;\n}\n"
      },
      "contracts/interfaces/ICurvePool2.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport './ICurvePoolPricable.sol';\n\ninterface ICurvePool2 is ICurvePoolPricable {\n    function coins(uint256 i) external view returns (address);\n\n    function add_liquidity(\n        uint256[2] memory amounts,\n        uint256 min_mint_amount\n    ) external returns (uint256);\n\n    function remove_liquidity(\n        uint256 burn_amount,\n        uint256[2] memory min_amounts\n    ) external returns (uint256[2] memory);\n\n    function remove_liquidity_imbalance(\n        uint256[2] memory amounts,\n        uint256 max_burn_amount\n    ) external returns (uint256);\n\n    function remove_liquidity_one_coin(\n        uint256 burn_amount,\n        int128 i,\n        uint256 min_received\n    ) external returns (uint256);\n\n    function exchange(\n        int128 i,\n        int128 j,\n        uint256 input,\n        uint256 min_output\n    ) external returns (uint256);\n\n    function exchange_underlying(int128 i, int128 j, uint256 input, uint256 min_output) external;\n\n    function calc_token_amount(\n        uint256[2] memory amounts,\n        bool is_deposit\n    ) external view returns (uint256);\n\n    function calc_token_amount(\n        uint256[2] memory amounts,\n        bool is_deposit,\n        bool previous\n    ) external view returns (uint256);\n\n    function calc_withdraw_one_coin(uint256 burn_amount, int128 i) external view returns (uint256);\n\n    function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/ICurvePoolN.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport './ICurvePoolPricable.sol';\n\ninterface ICurvePoolN is ICurvePoolPricable {\n    function coins(uint256 i) external view returns (address);\n\n    function add_liquidity(\n        uint256[] memory amounts,\n        uint256 min_mint_amount\n    ) external returns (uint256);\n\n    function remove_liquidity(\n        uint256 burn_amount,\n        uint256[] memory min_amounts\n    ) external returns (uint256[] memory);\n\n    function remove_liquidity_imbalance(\n        uint256[] memory amounts,\n        uint256 max_burn_amount\n    ) external returns (uint256);\n\n    function remove_liquidity_one_coin(\n        uint256 burn_amount,\n        int128 i,\n        uint256 min_received\n    ) external returns (uint256);\n\n    function exchange(\n        int128 i,\n        int128 j,\n        uint256 input,\n        uint256 min_output\n    ) external returns (uint256);\n\n    function exchange_underlying(int128 i, int128 j, uint256 input, uint256 min_output) external;\n\n    function calc_token_amount(\n        uint256[] memory amounts,\n        bool is_deposit\n    ) external view returns (uint256);\n\n    function calc_token_amount(\n        uint256[] memory amounts,\n        bool is_deposit,\n        bool previous\n    ) external view returns (uint256);\n\n    function calc_withdraw_one_coin(uint256 burn_amount, int128 i) external view returns (uint256);\n\n    function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/ICurvePoolPricable.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\ninterface ICurvePoolPricable {\n    function get_virtual_price() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IPool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport { IStrategy } from './IStrategy.sol';\n\ninterface IPool is IERC20 {\n    error WrongDeposit(uint256 sid, uint256[5] amounts);\n    error AbsentStrategy(uint256 sid);\n    error NotStartedStrategy(uint256 sid);\n    error DisabledStrategy(uint256 sid);\n    error WrongAmount();\n    error WrongWithdrawParams(uint256 sid);\n    error WrongRatio();\n    error ZeroAddress();\n    error DuplicatedStrategy();\n    error IncorrectArguments();\n    error WrongWithdrawPercent();\n    error WrongReceiver();\n    error IncorrectSid();\n    error WrongTokens();\n    error WrongDecimalMultipliers();\n\n    struct StrategyInfo {\n        IStrategy strategy;\n        uint256 startTime;\n        uint256 minted;\n        bool enabled;\n    }\n\n    event Deposited(\n        address indexed depositor,\n        uint256 deposited,\n        uint256[5] amounts,\n        uint256 indexed sid\n    );\n\n    event Withdrawn(address indexed withdrawer, uint256 withdrawn, uint256 indexed sid);\n\n    event FailedWithdrawal(address indexed withdrawer, uint256[5] amounts, uint256 withdrawn);\n\n    event AddedStrategy(uint256 indexed sid, address indexed strategyAddr, uint256 startTime);\n    event ClaimedRewards(address indexed receiver, IERC20[] rewardTokens);\n    event ClaimedExtraGains(address indexed receiver, uint256 amount);\n    event EnabledStrategy(address indexed pool);\n    event DisableStrategy(address indexed pool);\n    event UpdatedToken(\n        uint256 indexed tid,\n        address indexed token,\n        uint256 tokenDecimalMultiplier,\n        address tokenOld\n    );\n\n    function tokens() external view returns (IERC20[5] memory);\n\n    function token(uint256 tid) external view returns (IERC20);\n\n    function tokenDecimalsMultipliers() external view returns (uint256[5] memory);\n\n    function strategyInfo(uint256 sid) external view returns (StrategyInfo memory);\n\n    function claimRewards(address receiver, IERC20[] memory rewardTokens) external;\n\n    function totalHoldings() external view returns (uint256);\n\n    function strategyCount() external view returns (uint256);\n\n    function deposit(\n        uint256 sid,\n        uint256[5] memory amounts,\n        address receiver\n    ) external returns (uint256);\n\n    function depositStrategy(uint256 sid, uint256[5] memory amounts) external returns (uint256);\n\n    function withdraw(\n        uint256 sid,\n        uint256 stableAmount,\n        uint256[5] memory minTokenAmounts,\n        address receiver\n    ) external;\n\n    function mintAndClaimExtraGains(address receiver) external;\n}\n"
      },
      "contracts/interfaces/IStableConverter.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\ninterface IStableConverter {\n    function handle(address from, address to, uint256 amount, uint256 slippage) external;\n\n    function valuate(address from, address to, uint256 amount) external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IStakeDaoGauge.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\n\ninterface IStakeDaoGauge is IERC20 {\n    function claimable_reward(address _user, address _reward_token) external view returns (uint256);\n\n    function claim_rewards() external;\n\n    function withdraw(uint256 _value, address _addr, bool _claim_rewards) external;\n\n    function reward_tokens(uint256 id) external view returns (address);\n\n    function reward_count() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IStakeDaoVault.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport './IStakeDaoGauge.sol';\n\ninterface IStakeDaoVault is IERC20 {\n    function liquidityGauge() external view returns (IStakeDaoGauge);\n\n    function withdraw(uint256 amount) external;\n\n    function deposit(address staker, uint256 amount, bool earn) external;\n}\n"
      },
      "contracts/interfaces/IStrategy.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';\n\ninterface IStrategy {\n    function deposit(uint256[5] memory amounts) external returns (uint256);\n\n    function withdraw(\n        address receiver,\n        uint256 userDepositRatio, // multiplied by 1e18\n        uint256[5] memory minTokenAmounts\n    ) external returns (bool);\n\n    function withdrawAll(uint256[5] memory minTokenAmounts) external;\n\n    function totalHoldings() external view returns (uint256);\n\n    function claimRewards(address receiver, IERC20[] memory rewardTokens) external;\n\n    function calcTokenAmount(\n        uint256[5] memory tokenAmounts,\n        bool isDeposit\n    ) external view returns (uint256 sharesAmount);\n}\n"
      },
      "contracts/interfaces/ITokenConverter.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\ninterface ITokenConverter {\n    function handle(\n        address tokenIn,\n        address tokenOut,\n        uint256 amount,\n        uint256 minAmountOut\n    ) external returns (uint256);\n\n    function valuate(\n        address tokenIn_,\n        address tokenOut_,\n        uint256 amountIn_\n    ) external view returns (uint256);\n}\n"
      },
      "contracts/lib/Oracle/interfaces/IOracle.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity ^0.8.23;\n\ninterface IOracle {\n    /// @notice returns the price in USD of symbol.\n    function getUSDPrice(address token) external view returns (uint256);\n\n    /// @notice returns if the given token is supported for pricing.\n    function isTokenSupported(address token) external view returns (bool);\n}\n"
      },
      "contracts/strategies/curve/CurveNStratBase.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\n\nimport '../../interfaces/ICurvePoolN.sol';\nimport '../ZunamiStratBase.sol';\n\nabstract contract CurveNStratBase is ZunamiStratBase {\n    using SafeERC20 for IERC20;\n\n    ICurvePoolN public immutable pool;\n    IERC20 public immutable poolToken;\n\n    constructor(\n        IERC20[POOL_ASSETS] memory tokens_,\n        uint256[POOL_ASSETS] memory tokenDecimalsMultipliers_,\n        address poolAddr,\n        address poolTokenAddr\n    ) ZunamiStratBase(tokens_, tokenDecimalsMultipliers_) {\n        if (poolAddr == address(0)) revert ZeroAddress();\n        pool = ICurvePoolN(poolAddr);\n\n        if (poolTokenAddr == address(0)) revert ZeroAddress();\n        poolToken = IERC20(poolTokenAddr);\n    }\n\n    function convertCurvePoolTokenAmounts(\n        uint256[POOL_ASSETS] memory amounts\n    ) internal view virtual returns (uint256[] memory);\n\n    function depositLiquidity(\n        uint256[POOL_ASSETS] memory amounts\n    ) internal override returns (uint256 poolTokenAmount) {\n        uint256[] memory amountsN = convertAndApproveTokens(address(pool), amounts);\n        poolTokenAmount = depositCurve(amountsN);\n        depositBooster(poolTokenAmount);\n    }\n\n    function convertAndApproveTokens(\n        address pool,\n        uint256[POOL_ASSETS] memory amounts\n    ) internal virtual returns (uint256[] memory amountsN);\n\n    function depositCurve(uint256[] memory amountsN) internal virtual returns (uint256 deposited) {\n        return pool.add_liquidity(amountsN, 0);\n    }\n\n    function depositBooster(uint256 amount) internal virtual;\n\n    function getLiquidityTokenPrice() internal view virtual override returns (uint256) {\n        return getTokenPrice(address(poolToken));\n    }\n\n    function calcTokenAmount(\n        uint256[POOL_ASSETS] memory tokenAmounts,\n        bool isDeposit\n    ) public view override returns (uint256 sharesAmount) {\n        return pool.calc_token_amount(convertCurvePoolTokenAmounts(tokenAmounts), isDeposit);\n    }\n\n    function getCurveRemovingTokenIndex() internal view virtual returns (int128);\n\n    function getZunamiRemovingTokenIndex() internal view virtual returns (uint256);\n\n    function removeLiquidity(\n        uint256 amount,\n        uint256[POOL_ASSETS] memory minTokenAmounts,\n        bool\n    ) internal virtual override {\n        int128 curveTokenIndex = getCurveRemovingTokenIndex();\n        uint256 removedAmount = pool.remove_liquidity_one_coin(\n            amount,\n            curveTokenIndex,\n            minTokenAmounts[getZunamiRemovingTokenIndex()]\n        );\n        convertRemovedAmount(removedAmount);\n    }\n\n    function convertRemovedAmount(uint256 receivedAmount) internal virtual {}\n}\n"
      },
      "contracts/strategies/curve/stakeDao/aps/EmergencyAdminStakeDaoCurveNStratBase.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '../StakeDaoCurveNStratBase.sol';\n\nabstract contract EmergencyAdminStakeDaoCurveNStratBase is StakeDaoCurveNStratBase {\n    bytes32 public constant EMERGENCY_ADMIN_ROLE = keccak256('EMERGENCY_ADMIN_ROLE');\n\n    constructor(\n        IERC20[POOL_ASSETS] memory _tokens,\n        uint256[POOL_ASSETS] memory _tokenDecimalsMultipliers,\n        address _vaultAddr,\n        address _poolAddr,\n        address _poolTokenAddr\n    )\n        StakeDaoCurveNStratBase(\n            _tokens,\n            _tokenDecimalsMultipliers,\n            _vaultAddr,\n            _poolAddr,\n            _poolTokenAddr\n        )\n    {\n        _grantRole(EMERGENCY_ADMIN_ROLE, msg.sender);\n    }\n\n    function inflate(\n        uint256 ratioOfCrvLps,\n        uint256 minInflatedAmount\n    ) external onlyRole(EMERGENCY_ADMIN_ROLE) {\n        _inflate(ratioOfCrvLps, minInflatedAmount);\n    }\n\n    function _inflate(uint256 ratioOfCrvLps, uint256 minInflatedAmount) internal virtual;\n\n    function deflate(\n        uint256 ratioOfCrvLps,\n        uint256 minDeflateAmount\n    ) external onlyRole(EMERGENCY_ADMIN_ROLE) {\n        _deflate(ratioOfCrvLps, minDeflateAmount);\n    }\n\n    function _deflate(uint256 ratioOfCrvLps, uint256 minDeflateAmount) internal virtual;\n}\n"
      },
      "contracts/strategies/curve/stakeDao/aps/frxETH/FrxEthApsStakeDaoCurveStratBase.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '../../../../../utils/Constants.sol';\nimport '../../../../../interfaces/IController.sol';\nimport '../../../../../interfaces/IStableConverter.sol';\nimport '../EmergencyAdminStakeDaoCurveNStratBase.sol';\nimport '../../../../../interfaces/ICurvePool2.sol';\nimport '../../../../../interfaces/ITokenConverter.sol';\n\ncontract FrxEthApsStakeDaoCurveStratBase is EmergencyAdminStakeDaoCurveNStratBase {\n    using SafeERC20 for IERC20;\n\n    error InsufficientAmount();\n\n    uint256 constant ZUNAMI_STABLE_TOKEN_ID = 0;\n\n    uint128 public constant FRXETH_TOKEN_POOL_FRXETH_ID = 1;\n    int128 public constant FRXETH_TOKEN_POOL_FRXETH_ID_INT = int128(FRXETH_TOKEN_POOL_FRXETH_ID);\n\n    uint128 public constant FRXETH_TOKEN_POOL_TOKEN_ID = 0;\n    int128 public constant FRXETH_TOKEN_POOL_TOKEN_ID_INT = int128(FRXETH_TOKEN_POOL_TOKEN_ID);\n\n    IERC20 wEth = IERC20(Constants.WETH_ADDRESS);\n    IERC20 frxEth = IERC20(Constants.FRX_ETH_ADDRESS);\n\n    IController public immutable zunamiController;\n    IERC20 public immutable zunamiStable;\n\n    ITokenConverter public converter;\n    event SetTokenConverter(address tokenConverter);\n\n    constructor(\n        IERC20[POOL_ASSETS] memory _tokens,\n        uint256[POOL_ASSETS] memory _tokenDecimalsMultipliers,\n        address _vaultAddr,\n        address _poolAddr,\n        address _poolTokenAddr,\n        address _zunamiControllerAddr,\n        address _zunamiStableAddr\n    )\n        EmergencyAdminStakeDaoCurveNStratBase(\n            _tokens,\n            _tokenDecimalsMultipliers,\n            _vaultAddr,\n            _poolAddr,\n            _poolTokenAddr\n        )\n    {\n        if (_zunamiControllerAddr == address(0)) revert ZeroAddress();\n        zunamiController = IController(_zunamiControllerAddr);\n\n        if (_zunamiStableAddr == address(0)) revert ZeroAddress();\n        zunamiStable = IERC20(_zunamiStableAddr);\n    }\n\n    function setTokenConverter(address converterAddr) external onlyRole(DEFAULT_ADMIN_ROLE) {\n        if (address(converterAddr) == address(0)) revert ZeroAddress();\n        converter = ITokenConverter(converterAddr);\n        emit SetTokenConverter(converterAddr);\n    }\n\n    function getTokenPrice(address token) internal view override returns (uint256) {\n        return\n            (oracle.getUSDPrice(token) * 1e18) /\n            oracle.getUSDPrice(Constants.CHAINLINK_FEED_REGISTRY_ETH_ADDRESS);\n    }\n\n    function convertCurvePoolTokenAmounts(\n        uint256[POOL_ASSETS] memory amounts\n    ) internal pure override returns (uint256[] memory) {\n        uint256[] memory amountsN = new uint256[](8);\n        amountsN[FRXETH_TOKEN_POOL_TOKEN_ID] = amounts[ZUNAMI_STABLE_TOKEN_ID];\n        return amountsN;\n    }\n\n    function convertAndApproveTokens(\n        address pool,\n        uint256[POOL_ASSETS] memory amounts\n    ) internal override returns (uint256[] memory) {\n        uint256[] memory amountsN = new uint256[](8);\n        amountsN[FRXETH_TOKEN_POOL_TOKEN_ID] = amounts[ZUNAMI_STABLE_TOKEN_ID];\n        zunamiStable.safeIncreaseAllowance(pool, amountsN[FRXETH_TOKEN_POOL_TOKEN_ID]);\n        return amountsN;\n    }\n\n    function getCurveRemovingTokenIndex() internal pure override returns (int128) {\n        return FRXETH_TOKEN_POOL_TOKEN_ID_INT;\n    }\n\n    function getZunamiRemovingTokenIndex() internal pure override returns (uint256) {\n        return ZUNAMI_STABLE_TOKEN_ID;\n    }\n\n    function _inflate(uint256 ratioOfCrvLps, uint256 minInflatedAmount) internal override {\n        uint256 removingCrvLps = getLiquidityAmountByRatio(ratioOfCrvLps);\n        depositedLiquidity -= removingCrvLps;\n        vault.withdraw(removingCrvLps);\n\n        uint256 frxEthAmount = pool.remove_liquidity_one_coin(\n            removingCrvLps,\n            FRXETH_TOKEN_POOL_FRXETH_ID_INT,\n            0\n        );\n\n        frxEth.safeIncreaseAllowance(address(zunamiController), frxEthAmount);\n        uint256 zunStableAmount = zunamiController.deposit(\n            [0, frxEthAmount, 0, 0, 0],\n            address(this)\n        );\n\n        if (zunStableAmount < minInflatedAmount) {\n            revert InsufficientAmount();\n        }\n\n        uint256[] memory amountsN = new uint256[](8);\n        amountsN[FRXETH_TOKEN_POOL_TOKEN_ID] = zunStableAmount;\n        zunamiStable.safeIncreaseAllowance(address(pool), zunStableAmount);\n\n        uint256 poolTokenAmount = depositCurve(amountsN);\n        depositedLiquidity += poolTokenAmount;\n        depositBooster(poolTokenAmount);\n    }\n\n    function _deflate(uint256 ratioOfCrvLps, uint256 minDeflateAmount) internal override {\n        uint256 removingCrvLps = getLiquidityAmountByRatio(ratioOfCrvLps);\n        depositedLiquidity -= removingCrvLps;\n        vault.withdraw(removingCrvLps);\n\n        uint256 tokenAmount = pool.remove_liquidity_one_coin(\n            removingCrvLps,\n            FRXETH_TOKEN_POOL_TOKEN_ID_INT,\n            0\n        );\n\n        zunamiStable.safeIncreaseAllowance(address(zunamiController), tokenAmount);\n        zunamiController.withdraw(tokenAmount, [uint256(0), 0, 0, 0, 0], address(this));\n\n        uint256 wEthBalance = wEth.balanceOf(address(this));\n        if (wEthBalance > 0) {\n            wEth.safeTransfer(address(converter), wEthBalance);\n            converter.handle(\n                address(wEth),\n                address(frxEth),\n                wEthBalance,\n                applySlippage(wEthBalance)\n            );\n        }\n\n        uint256 frxEthAmount = frxEth.balanceOf(address(this));\n\n        if (frxEthAmount < minDeflateAmount) {\n            revert InsufficientAmount();\n        }\n\n        uint256[] memory amountsN = new uint256[](8);\n        amountsN[FRXETH_TOKEN_POOL_FRXETH_ID] = frxEthAmount;\n        frxEth.safeIncreaseAllowance(address(pool), frxEthAmount);\n\n        uint256 poolTokenAmount = depositCurve(amountsN);\n        depositedLiquidity += poolTokenAmount;\n        depositBooster(poolTokenAmount);\n    }\n}\n"
      },
      "contracts/strategies/curve/stakeDao/aps/frxETH/ZunEthFrxEthApsStakeDaoCurveStrat.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '../../../../../utils/Constants.sol';\nimport './FrxEthApsStakeDaoCurveStratBase.sol';\n\ncontract ZunEthFrxEthApsStakeDaoCurveStrat is FrxEthApsStakeDaoCurveStratBase {\n    constructor()\n        FrxEthApsStakeDaoCurveStratBase(\n            [\n                IERC20(Constants.zunETH_ADDRESS),\n                IERC20(address(0)),\n                IERC20(address(0)),\n                IERC20(address(0)),\n                IERC20(address(0))\n            ],\n            [uint256(1), 0, 0, 0, 0],\n            Constants.SDT_zunETH_frxETH_VAULT_ADDRESS,\n            Constants.CRV_zunETH_frxETH_ADDRESS,\n            Constants.CRV_zunETH_frxETH_LP_ADDRESS,\n            Constants.zunETH_CONTROLLER_ADDRESS,\n            Constants.zunETH_ADDRESS\n        )\n    {}\n}\n"
      },
      "contracts/strategies/curve/stakeDao/StakeDaoCurveNStratBase.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '../CurveNStratBase.sol';\nimport '../../../interfaces/IStakeDaoVault.sol';\n\nabstract contract StakeDaoCurveNStratBase is CurveNStratBase {\n    using SafeERC20 for IERC20;\n\n    IStakeDaoVault public immutable vault;\n\n    constructor(\n        IERC20[POOL_ASSETS] memory _tokens,\n        uint256[POOL_ASSETS] memory _tokenDecimalsMultipliers,\n        address _vaultAddr,\n        address _poolAddr,\n        address _poolTokenAddr\n    ) CurveNStratBase(_tokens, _tokenDecimalsMultipliers, _poolAddr, _poolTokenAddr) {\n        if (_vaultAddr == address(0)) revert ZeroAddress();\n        vault = IStakeDaoVault(_vaultAddr);\n    }\n\n    function depositBooster(uint256 amount) internal override {\n        poolToken.safeIncreaseAllowance(address(vault), amount);\n        vault.deposit(address(this), amount, true);\n    }\n\n    function removeLiquidity(\n        uint256 amount,\n        uint256[POOL_ASSETS] memory minTokenAmounts,\n        bool removeAll\n    ) internal virtual override {\n        vault.withdraw(amount);\n        super.removeLiquidity(amount, minTokenAmounts, removeAll);\n    }\n\n    function claimCollectedRewards() internal virtual override {\n        vault.liquidityGauge().claim_rewards();\n    }\n}\n"
      },
      "contracts/strategies/ZunamiPoolAccessControl.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '@openzeppelin/contracts/access/AccessControl.sol';\nimport '../interfaces/IPool.sol';\n\ncontract ZunamiPoolAccessControl is AccessControl {\n    error MustBeCalledByZunamiPool();\n    error ZeroAddress();\n    error ZeroValue();\n    error MustHaveOwner();\n\n    IPool public zunamiPool;\n\n    event ZunamiPoolSet(address zunamiPoolAddr);\n\n    modifier onlyZunamiPool() {\n        if (_msgSender() != address(zunamiPool)) revert MustBeCalledByZunamiPool();\n        _;\n    }\n\n    constructor() {\n        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\n    }\n\n    function setZunamiPool(address zunamiAddr) external onlyRole(DEFAULT_ADMIN_ROLE) {\n        if (zunamiAddr == address(0)) revert ZeroAddress();\n        zunamiPool = IPool(zunamiAddr);\n        emit ZunamiPoolSet(zunamiAddr);\n    }\n}\n"
      },
      "contracts/strategies/ZunamiStratBase.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\n\nimport '../lib/Oracle/interfaces/IOracle.sol';\nimport '../interfaces/IStrategy.sol';\nimport './ZunamiPoolAccessControl.sol';\n\nabstract contract ZunamiStratBase is IStrategy, ZunamiPoolAccessControl {\n    using SafeERC20 for IERC20;\n\n    error WrongTokens();\n    error WrongDecimalMultipliers();\n    error DepositLiquidityValueTooLow();\n\n    uint8 public constant POOL_ASSETS = 5;\n    uint256 public constant RATIO_MULTIPLIER = 1e18;\n    uint256 public constant PRICE_DENOMINATOR = 1e18;\n\n    uint256 public constant SLIPPAGE_DENOMINATOR = 10000;\n    uint256 public slippage = 50; // 0.50%\n\n    IERC20[POOL_ASSETS] public tokens;\n    uint256[POOL_ASSETS] public tokenDecimalsMultipliers;\n    IOracle public oracle;\n\n    uint256 public depositedLiquidity;\n\n    event SlippageSet(uint256 oldSlippage, uint256 newSlippage);\n    event PriceOracleSet(address oracleAddr);\n\n    constructor(\n        IERC20[POOL_ASSETS] memory tokens_,\n        uint256[POOL_ASSETS] memory tokenDecimalsMultipliers_\n    ) {\n        bool otherZeros = false;\n        for (uint256 i = 0; i < POOL_ASSETS; i++) {\n            if (otherZeros && address(tokens_[i]) != address(0)) revert WrongTokens();\n            if (address(tokens_[i]) == address(0)) otherZeros = true;\n            if (\n                (address(tokens_[i]) != address(0) && tokenDecimalsMultipliers_[i] == 0) ||\n                (address(tokens_[i]) == address(0) && tokenDecimalsMultipliers_[i] != 0)\n            ) revert WrongDecimalMultipliers();\n        }\n\n        tokens = tokens_;\n        tokenDecimalsMultipliers = tokenDecimalsMultipliers_;\n    }\n\n    function setPriceOracle(address oracleAddr) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        if (oracleAddr == address(0)) revert ZeroAddress();\n\n        oracle = IOracle(oracleAddr);\n        emit PriceOracleSet(oracleAddr);\n    }\n\n    function setSlippage(uint256 _slippage) external onlyRole(DEFAULT_ADMIN_ROLE) {\n        require(_slippage > 0 && _slippage <= SLIPPAGE_DENOMINATOR, 'Wrong slippage number!');\n        emit SlippageSet(slippage, _slippage);\n        slippage = _slippage;\n    }\n\n    function applySlippage(uint256 amount) internal view returns (uint256) {\n        return (amount * (SLIPPAGE_DENOMINATOR - slippage)) / SLIPPAGE_DENOMINATOR;\n    }\n\n    function calcTokenAmount(\n        uint256[POOL_ASSETS] memory tokenAmounts,\n        bool isDeposit\n    ) public view virtual returns (uint256 sharesAmount);\n\n    function calcLiquidityValue(uint256 tokenAmount) internal view returns (uint256) {\n        return (tokenAmount * getLiquidityTokenPrice()) / PRICE_DENOMINATOR;\n    }\n\n    function getLiquidityTokenPrice() internal view virtual returns (uint256);\n\n    function getTokenPrice(address token) internal view virtual returns (uint256) {\n        return oracle.getUSDPrice(token);\n    }\n\n    function totalHoldings() public view virtual returns (uint256) {\n        return calcLiquidityValue(getLiquidityBalance());\n    }\n\n    function getLiquidityBalance() internal view virtual returns (uint256) {\n        return depositedLiquidity;\n    }\n\n    function deposit(\n        uint256[POOL_ASSETS] memory amounts\n    ) external onlyZunamiPool returns (uint256 liquidityValue) {\n        uint256 depositValue = valuateDeposit(amounts);\n        uint256 liquidity = depositLiquidity(amounts);\n        depositedLiquidity += liquidity;\n        liquidityValue = calcLiquidityValue(liquidity);\n        if (liquidityValue < applySlippage(depositValue)) revert DepositLiquidityValueTooLow();\n    }\n\n    function valuateDeposit(\n        uint256[POOL_ASSETS] memory amounts\n    ) internal view virtual returns (uint256 value) {\n        for (uint256 i = 0; i < POOL_ASSETS; i++) {\n            value +=\n                (getTokenPrice(address(tokens[i])) * amounts[i] * tokenDecimalsMultipliers[i]) /\n                PRICE_DENOMINATOR;\n        }\n        return value;\n    }\n\n    function depositLiquidity(\n        uint256[POOL_ASSETS] memory amounts\n    ) internal virtual returns (uint256);\n\n    function getLiquidityAmountByRatio(\n        uint256 poolTokenRatio // multiplied by 1e18\n    ) internal view returns (uint256) {\n        require(poolTokenRatio > 0 && poolTokenRatio <= RATIO_MULTIPLIER, 'Wrong PoolToken Ratio');\n        return (getLiquidityBalance() * poolTokenRatio) / RATIO_MULTIPLIER;\n    }\n\n    function withdraw(\n        address receiver,\n        uint256 poolTokenRatio, // multiplied by 1e18\n        uint256[POOL_ASSETS] memory tokenAmounts\n    ) external virtual onlyZunamiPool returns (bool) {\n        uint256 liquidityAmount = getLiquidityAmountByRatio(poolTokenRatio);\n\n        if (liquidityAmount < calcTokenAmount(tokenAmounts, false)) {\n            return false;\n        }\n\n        uint256[] memory prevBalances = new uint256[](POOL_ASSETS);\n        for (uint256 i = 0; i < POOL_ASSETS; i++) {\n            if (address(tokens[i]) == address(0)) break;\n            prevBalances[i] = tokens[i].balanceOf(address(this));\n        }\n\n        depositedLiquidity -= liquidityAmount;\n        removeLiquidity(liquidityAmount, tokenAmounts, false);\n\n        transferTokensOut(convertTokensToDynamic(tokens), receiver, prevBalances);\n\n        return true;\n    }\n\n    function removeLiquidity(\n        uint256 amount,\n        uint256[POOL_ASSETS] memory minTokenAmounts,\n        bool removeAll\n    ) internal virtual;\n\n    function claimRewards(\n        address receiver,\n        IERC20[] memory rewardTokens\n    ) public virtual onlyZunamiPool {\n        claimCollectedRewards();\n        transferTokensOut(rewardTokens, receiver, fillArrayN(0, rewardTokens.length));\n    }\n\n    function claimCollectedRewards() internal virtual {}\n\n    function withdrawAll(\n        uint256[POOL_ASSETS] memory minTokenAmounts\n    ) external virtual onlyZunamiPool {\n        removeLiquidity(depositedLiquidity, minTokenAmounts, true);\n        depositedLiquidity = 0;\n        transferTokensOut(convertTokensToDynamic(tokens), _msgSender(), fillArrayN(0, POOL_ASSETS));\n    }\n\n    function transferTokensOut(\n        IERC20[] memory transferringTokens,\n        address receiver,\n        uint256[] memory prevBalances\n    ) internal {\n        uint256 transferAmount;\n        IERC20 token_;\n        for (uint256 i = 0; i < transferringTokens.length; i++) {\n            token_ = transferringTokens[i];\n            if (address(token_) == address(0)) break;\n            transferAmount = token_.balanceOf(address(this)) - prevBalances[i];\n            if (transferAmount > 0) {\n                token_.safeTransfer(receiver, transferAmount);\n            }\n        }\n    }\n\n    function convertTokensToDynamic(\n        IERC20[POOL_ASSETS] memory _tokens\n    ) internal pure returns (IERC20[] memory tokesDynamic) {\n        tokesDynamic = new IERC20[](POOL_ASSETS);\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            tokesDynamic[i] = _tokens[i];\n        }\n    }\n\n    function fillArrayN(\n        uint256 _value,\n        uint256 _count\n    ) internal pure returns (uint256[] memory values) {\n        values = new uint256[](_count);\n        for (uint256 i = 0; i < _count; i++) {\n            values[i] = _value;\n        }\n    }\n\n    function withdrawStuckToken(IERC20 _token) external onlyRole(DEFAULT_ADMIN_ROLE) {\n        uint256 tokenBalance = _token.balanceOf(address(this));\n        if (tokenBalance > 0) {\n            _token.safeTransfer(_msgSender(), tokenBalance);\n        }\n    }\n}\n"
      },
      "contracts/utils/Constants.sol": {
        "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.23;\n\nlibrary Constants {\n    address internal constant CRVUSD_ADDRESS = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E;\n    address internal constant USDC_ADDRESS = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;\n    address internal constant USDT_ADDRESS = 0xdAC17F958D2ee523a2206206994597C13D831ec7;\n    address internal constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;\n    address internal constant FRX_ETH_ADDRESS = 0x5E8422345238F34275888049021821E8E08CAa1f;\n    address internal constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\n    address internal constant CVX_ADDRESS = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;\n    address internal constant CRV_ADDRESS = 0xD533a949740bb3306d119CC777fa900bA034cd52;\n    address internal constant FXS_ADDRESS = 0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0;\n    address internal constant SPELL_ADDRESS = 0x090185f2135308BaD17527004364eBcC2D37e5F6;\n    address internal constant SDT_ADDRESS = 0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F;\n    address internal constant SFRXETH_ADDRESS = 0xac3E018457B222d93114458476f3E3416Abbe38F;\n    // Will be added after deployment of zunUSD v2 pool\n    address internal constant ZUNUSD_ADDRESS = 0x8C0D76C9B18779665475F3E212D9Ca1Ed6A1A0e6;\n    // Will be added after deployment of zunUSD v2 pool controller\n    address internal constant zunUSD_CONTROLLER_ADDRESS =\n        0x618eee502CDF6b46A2199C21D1411f3F6065c940;\n    // Will be added after deployment of zunETH v2 pool\n    address internal constant zunETH_ADDRESS = 0xc2e660C62F72c2ad35AcE6DB78a616215E2F2222;\n    address internal constant zunETH_CONTROLLER_ADDRESS =\n        0x54A00DA65c79DDCe24E7fe4691737FD70F7797DF;\n\n    address public constant CHAINLINK_FEED_REGISTRY_ETH_ADDRESS =\n        0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    address internal constant CRV_3POOL_ADDRESS = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;\n    address internal constant CRV_3POOL_LP_ADDRESS = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490;\n\n    address internal constant CRV_TRICRYPTO2_ADDRESS = 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46;\n\n    address internal constant ETH_frxETH_ADDRESS = 0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577;\n    address internal constant ETH_frxETH_LP_ADDRESS = 0xf43211935C781D5ca1a41d2041F397B8A7366C7A;\n\n    address internal constant WETH_frxETH_ADDRESS = 0x9c3B46C0Ceb5B9e304FCd6D88Fc50f7DD24B31Bc;\n    address internal constant WETH_frxETH_LP_ADDRESS = 0x9c3B46C0Ceb5B9e304FCd6D88Fc50f7DD24B31Bc;\n\n    address internal constant CRV_FRAX_USDC_POOL_ADDRESS =\n        0xDcEF968d416a41Cdac0ED8702fAC8128A64241A2;\n    address internal constant CRV_FRAX_USDC_POOL_LP_ADDRESS =\n        0x3175Df0976dFA876431C2E9eE6Bc45b65d3473CC;\n\n    address internal constant SDT_CRVUSD_USDT_VAULT_ADDRESS =\n        0x37b24ac19504C0c6FC1ADc8deb5D24f5C4F6A2f2;\n    address internal constant CRV_CRVUSD_USDT_LP_ADDRESS =\n        0x390f3595bCa2Df7d23783dFd126427CCeb997BF4;\n    address internal constant CRV_CRVUSD_USDT_ADDRESS = 0x390f3595bCa2Df7d23783dFd126427CCeb997BF4;\n\n    address internal constant SDT_CRVUSD_USDC_VAULT_ADDRESS =\n        0xb618EA40cb1F5b08839Ba228C8dd58AC3DCA12F3;\n    address internal constant CRV_CRVUSD_USDC_LP_ADDRESS =\n        0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E;\n    address internal constant CRV_CRVUSD_USDC_ADDRESS = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E;\n\n    address internal constant CRV_BOOSTER_ADDRESS = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31;\n\n    address internal constant CRV_ETH_stETH_ADDRESS = 0x21E27a5E5513D6e65C4f830167390997aA84843a;\n    address internal constant CRV_ETH_stETH_LP_ADDRESS = 0x21E27a5E5513D6e65C4f830167390997aA84843a;\n    address internal constant CVX_ETH_stETH_REWARDS_ADDRESS =\n        0x6B27D7BC63F1999D14fF9bA900069ee516669ee8;\n    uint256 internal constant CVX_ETH_stETH_PID = 177;\n\n    address internal constant CRV_zunUSD_crvUSD_ADDRESS =\n        0x8C24b3213FD851db80245FCCc42c40B94Ac9a745;\n    address internal constant CRV_zunUSD_crvUSD_LP_ADDRESS =\n        0x8C24b3213FD851db80245FCCc42c40B94Ac9a745;\n    address internal constant CVX_zunUSD_crvUSD_REWARDS_ADDRESS =\n        0xB0408d1477554268Ece9b0a40290C345196fBf1B;\n    uint256 internal constant CVX_zunUSD_crvUSD_PID = 309;\n\n    address internal constant CRV_USDT_crvUSD_ADDRESS = 0x390f3595bCa2Df7d23783dFd126427CCeb997BF4;\n\n    address internal constant LLAMALEND_CRVUSD_ADDRESS = 0xCeA18a8752bb7e7817F9AE7565328FE415C0f2cA;\n    address internal constant CVX_LLAMALEND_CRVUSD_REWARDS_ADDRESS =\n        0x4bf2d8484474170bff8a8c34475be3d87dFF28cA;\n    uint256 internal constant CVX_LLAMALEND_CRVUSD_PID = 325;\n\n    address internal constant CRV_zunETH_frxETH_ADDRESS =\n        0x3A65cbaebBFecbeA5D0CB523ab56fDbda7fF9aAA;\n    address internal constant CRV_zunETH_frxETH_LP_ADDRESS =\n        0x3A65cbaebBFecbeA5D0CB523ab56fDbda7fF9aAA;\n    address internal constant CVX_zunETH_frxETH_REWARDS_ADDRESS =\n        0x756d67A10974Fa0e0cE63F82AF4E7ef0d46d452D;\n    uint256 internal constant CVX_zunETH_frxETH_PID = 330;\n\n    address internal constant SDT_zunETH_frxETH_VAULT_ADDRESS =\n        0xAaE1Ae12d4C8b811DDa1188b01be23b4ab7C62D2;\n\n    address internal constant SDT_LLAMALEND_CRVUSD_VAULT_ADDRESS =\n        0xfa6D40573082D797CB3cC378c0837fB90eB043e5;\n\n    // Will be added after deployment of zunUSD v2 pool and curve pool for zunUSD\n    address internal constant CRV_zunUSD_crvFRAX_ADDRESS = address(0);\n    address internal constant CRV_zunUSD_crvFRAX_LP_ADDRESS = address(0);\n    address internal constant CVX_zunUSD_crvFRAX_REWARDS_ADDRESS = address(0);\n    uint256 internal constant CVX_zunUSD_crvFRAX_PID = 0;\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {}
    }
  }}