creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin-4.5.0/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "@openzeppelin-4.5.0/contracts/security/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    constructor() {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\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        _status = _NOT_ENTERED;\n    }\n}\n"
      },
      "@openzeppelin-4.5.0/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^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 `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 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 `from` to `to` 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(\n        address from,\n        address to,\n        uint256 amount\n    ) 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-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.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 Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) 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(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) 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        require(\n            (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(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\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, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "@openzeppelin-4.5.0/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\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     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 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        (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(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) 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(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) 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(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) 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        (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(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\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(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal 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                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-4.5.0/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\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-4.5.0/contracts/utils/math/SafeMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\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        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\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        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\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        unchecked {\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    /**\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        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\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        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\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        return a + b;\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        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        return a * b;\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.\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\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        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(\n        uint256 a,\n        uint256 b,\n        string memory errorMessage\n    ) internal pure returns (uint256) {\n        unchecked {\n            require(b <= a, errorMessage);\n            return a - b;\n        }\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     * 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(\n        uint256 a,\n        uint256 b,\n        string memory errorMessage\n    ) internal pure returns (uint256) {\n        unchecked {\n            require(b > 0, errorMessage);\n            return a / b;\n        }\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(\n        uint256 a,\n        uint256 b,\n        string memory errorMessage\n    ) internal pure returns (uint256) {\n        unchecked {\n            require(b > 0, errorMessage);\n            return a % b;\n        }\n    }\n}\n"
      },
      "contracts/ICakeV3.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin-4.5.0/contracts/access/Ownable.sol\";\nimport \"@openzeppelin-4.5.0/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ninterface IVeCake {\n    function getUserInfo(address _user) external view returns (\n        int128 amount,\n        uint256 end,\n        address cakePoolProxy,\n        uint128 cakeAmount,\n        uint48 lockEndTime,\n        uint48 migrationTime,\n        uint16 cakePoolType,\n        uint16 withdrawFlag\n    );\n\n    function balanceOfAtTime(address _user, uint256 _timestamp) external view returns (uint256);\n}\n\ninterface IIFODeployer {\n    function currIFOAddress() external view returns (address);\n}\n\ninterface IIFOInitializable {\n    function endTimestamp() external view returns (uint256);\n}\n\ncontract ICakeV3 is Ownable {\n    using SafeMath for uint256;\n\n    address public admin;\n\n    address public immutable veCakeAddress;\n\n    address public ifoDeployerAddress;\n\n    uint256 public ratio;\n    uint256 public constant RATION_PRECISION = 1000;\n\n    uint256 public constant MIN_CEILING_DURATION = 1 weeks;\n\n    event UpdateRatio(uint256 newRatio);\n    event UpdateIfoDeployerAddress(address indexed newAddress);\n\n    /**\n     * @notice Constructor\n     * @param _veCakeAddress: veCake contract\n     */\n    constructor(\n        address _veCakeAddress\n    ) public {\n        veCakeAddress = _veCakeAddress;\n        admin = owner();\n        ratio = 1000;\n    }\n\n    /**\n     * @notice calculate iCake credit per user.\n     * @param _user: user address.\n     * @param _endTime: user lock end time on veCake contract.\n     */\n    function getUserCreditWithTime(address _user, uint256 _endTime) external view returns (uint256) {\n        require(_user != address(0), \"getUserCredit: Invalid user address\");\n\n        // require the end time must be in the future\n        // require(_endTime > block.timestamp, \"end must be in future\");\n        // instead let's filter the time to current if too old\n        if (_endTime <= block.timestamp){\n            _endTime = block.timestamp;\n        }\n\n        return _sumUserCredit(_user, _endTime);\n    }\n\n    /**\n     * @notice calculate iCake credit per user with Ifo address.\n     * @param _user: user address.\n     * @param _ifo: the ifo contract.\n     */\n    function getUserCreditWithIfoAddr(address _user, address _ifo) external view returns (uint256) {\n        require(_user != address(0), \"getUserCredit: Invalid user address\");\n        require(_ifo != address(0), \"getUserCredit: Invalid ifo address\");\n\n        uint256 _endTime = IIFOInitializable(_ifo).endTimestamp();\n\n        if (_endTime <= block.timestamp){\n            _endTime = block.timestamp;\n        }\n\n        return _sumUserCredit(_user, _endTime);\n    }\n\n    /**\n     * @notice calculate iCake credit per user for next ifo.\n     * @param _user: user address.\n     */\n    function getUserCreditForNextIfo(address _user) external view returns (uint256) {\n        require(_user != address(0), \"getUserCredit: Invalid user address\");\n\n        address currIFOAddress = IIFODeployer(ifoDeployerAddress).currIFOAddress();\n\n        uint256 _endTime = block.timestamp;\n        if (currIFOAddress != address(0)) {\n            _endTime = IIFOInitializable(currIFOAddress).endTimestamp();\n\n            if (_endTime <= block.timestamp){\n                _endTime = block.timestamp;\n            }\n        }\n\n        return _sumUserCredit(_user, _endTime);\n    }\n\n    function getUserCredit(address _user) external view returns (uint256) {\n        require(_user != address(0), \"getUserCredit: Invalid user address\");\n\n        uint256 _endTime = IIFOInitializable(msg.sender).endTimestamp();\n\n        return _sumUserCredit(_user, _endTime);\n    }\n\n    /**\n     * @notice update ratio for iCake calculation.\n     * @param _newRatio: new ratio\n     */\n    function updateRatio(uint256 _newRatio) external onlyOwner {\n        require(_newRatio <= RATION_PRECISION, \"updateRatio: Invalid ratio\");\n        require(ratio != _newRatio, \"updateRatio: Ratio not changed\");\n        ratio = _newRatio;\n        emit UpdateRatio(ratio);\n    }\n\n    /**\n     * @notice update deployer address of IFO.\n     * @param _newAddress: new deployer address\n     */\n    function updateIfoDeployerAddress(address _newAddress) external onlyOwner {\n        require(_newAddress != address(0), \"updateIfoDeployerAddress: Address can not be empty\");\n        ifoDeployerAddress = _newAddress;\n        emit UpdateIfoDeployerAddress(_newAddress);\n    }\n\n    /**\n     * @notice get user and proxy credit from veCake contract and sum together\n     * @param _user user's address\n     * @param _endTime timestamp to calculate user's veCake amount\n     */\n    function _sumUserCredit(address _user, uint256 _endTime) internal view returns (uint256) {\n        // get native\n        uint256 veNative = IVeCake(veCakeAddress).balanceOfAtTime(_user, _endTime);\n\n        // get proxy/migrated\n        uint256 veMigrate = 0;\n        ( , ,address cakePoolProxy, , , , , )  = IVeCake(veCakeAddress).getUserInfo(_user);\n        if (cakePoolProxy != address(0)) {\n            veMigrate = IVeCake(veCakeAddress).balanceOfAtTime(cakePoolProxy, _endTime);\n        }\n\n        return (veNative + veMigrate) * ratio / RATION_PRECISION;\n    }\n}\n"
      },
      "contracts/IFODeployerV8.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport \"@openzeppelin-4.5.0/contracts/access/Ownable.sol\";\nimport \"@openzeppelin-4.5.0/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IFOInitializableV8.sol\";\n\n/**\n * @title IFODeployerV8\n */\ncontract IFODeployerV8 is Ownable {\n    using SafeERC20 for IERC20;\n\n    uint256 public constant MAX_BUFFER_SECONDS = 86400 * 7; // (7 days on BSC)\n    uint256 public constant MAX_BUFFER_SECONDS_VESTING = 86400 * 365; // (365 days on BSC)\n\n    address public currIFOAddress;\n\n    event AdminTokenRecovery(address indexed tokenRecovered, uint256 amount);\n    event NewIFOContract(address indexed ifoAddress);\n\n    error LpTokenSameWithOfferingToken();\n    error AdminAddressIsNull();\n    error EndTimeTooFar();\n    error StartTimeMustInferiorToEndTime();\n    error StartTimeMustGreaterThanCurrentBlockTime();\n    error VestingStartTimeMustGreaterThanEndTime();\n    error VestingStartTimeTooFar();\n\n    /**\n     * @notice It creates the IFO contract and initializes the contract.\n     * @dev It can only be called once.\n     * @param _addresses: [0] lpToken [1] offeringToken [2] pancakeProfile [3] iCake [4] adminAddress [5] admissionProfile\n     * @param _startAndEndTimestamps: [0] startTimestamp [1] endTimestamp\n     * @param _maxPoolId: maximum id of pools, sometimes only public sale exist\n     * @param _pointThreshold: threshold of user's point in pancake profile\n     * @param _vestingStartTime: the start timestamp of vesting\n     */\n    function createIFO(\n        address[] calldata _addresses,\n        uint256[] calldata _startAndEndTimestamps,\n        uint8 _maxPoolId,\n        uint256 _pointThreshold,\n        uint256 _vestingStartTime\n    ) external onlyOwner {\n        require(IERC20(_addresses[0]).totalSupply() >= 0);\n        require(IERC20(_addresses[1]).totalSupply() >= 0);\n        if (_addresses[0] == _addresses[1]) revert LpTokenSameWithOfferingToken();\n        if (_addresses[4] == address(0)) revert AdminAddressIsNull();\n        if (_startAndEndTimestamps[1] >= (block.timestamp + MAX_BUFFER_SECONDS)) revert EndTimeTooFar();\n        if (_startAndEndTimestamps[0] >= _startAndEndTimestamps[1]) revert StartTimeMustInferiorToEndTime();\n        if (_startAndEndTimestamps[0] <= block.timestamp) revert StartTimeMustGreaterThanCurrentBlockTime();\n        if (_vestingStartTime != 0) {\n            if (_vestingStartTime < _startAndEndTimestamps[1] + 30 days) revert VestingStartTimeMustGreaterThanEndTime();\n            if (_vestingStartTime > _startAndEndTimestamps[1] + MAX_BUFFER_SECONDS_VESTING) revert VestingStartTimeTooFar();\n        }\n\n        bytes memory bytecode = type(IFOInitializableV8).creationCode;\n        bytes32 salt = keccak256(abi.encodePacked(_addresses[0], _addresses[1], _startAndEndTimestamps[0]));\n        address ifoAddress;\n\n        assembly {\n            ifoAddress := create2(0, add(bytecode, 32), mload(bytecode), salt)\n        }\n\n        IFOInitializableV8(ifoAddress).initialize(\n            _addresses,\n            _startAndEndTimestamps,\n            MAX_BUFFER_SECONDS,\n            _maxPoolId,\n            _pointThreshold,\n            _vestingStartTime\n        );\n\n        if (currIFOAddress != ifoAddress) {\n            currIFOAddress = ifoAddress;\n        }\n\n        emit NewIFOContract(ifoAddress);\n    }\n\n    /**\n     * @notice It allows the admin to recover wrong tokens sent to the contract\n     * @param _tokenAddress: the address of the token to withdraw\n     * @dev This function is only callable by admin.\n     */\n    function recoverWrongTokens(address _tokenAddress) external onlyOwner {\n        uint256 balanceToRecover = IERC20(_tokenAddress).balanceOf(address(this));\n        require(balanceToRecover > 0, \"Operations: Balance must be > 0\");\n        IERC20(_tokenAddress).safeTransfer(address(msg.sender), balanceToRecover);\n\n        emit AdminTokenRecovery(_tokenAddress, balanceToRecover);\n    }\n}"
      },
      "contracts/IFOInitializableV8.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin-4.5.0/contracts/security/ReentrancyGuard.sol\";\nimport \"@openzeppelin-4.5.0/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./interfaces/IIFOV8.sol\";\nimport \"./libraries/IFOLibV8.sol\";\nimport \"./utils/WhiteListV2.sol\";\nimport \"./interfaces/IPancakeProfile.sol\";\nimport \"./ICakeV3.sol\";\n\n/**\n * @title IFOInitializableV8\n */\ncontract IFOInitializableV8 is IIFOV8, ReentrancyGuard, Whitelist {\n    using SafeERC20 for IERC20;\n\n    // The address of the smart chef factory\n    address private immutable IFO_FACTORY;\n\n    // Whether it is initialized\n    bool private isInitialized;\n\n    // all the addresses\n    // [0] lpToken [1] offeringToken [2] pancakeProfile [3] iCake [4] adminAddress [5] admissionProfile\n    address[6] public addresses;\n\n    // The timestamp when IFO starts\n    uint256 public startTimestamp;\n\n    // The timestamp when IFO ends\n    uint256 public endTimestamp;\n\n    // Max buffer seconds (for sanity checks)\n    uint256 public MAX_BUFFER_SECONDS;\n\n    // Max pool id (sometimes only public sale exist)\n    uint8 public MAX_POOL_ID;\n\n    // The minimum point special sale require\n    uint256 public pointThreshold;\n\n    // point config\n    PointConfig public pointConfig;\n\n    // Total tokens distributed across the pools\n    uint256 public totalTokensOffered;\n\n    // Struct that contains each pool characteristics\n    struct PoolCharacteristics {\n        uint256 raisingAmountPool; // amount of tokens raised for the pool (in LP tokens)\n        uint256 offeringAmountPool; // amount of tokens offered for the pool (in offeringTokens)\n        uint256 limitPerUserInLP; // limit of tokens per user (if 0, it is ignored)\n        bool hasTax; // tax on the overflow (if any, it works with _calculateTaxOverflow)\n        uint256 flatTaxRate; // new rate for flat tax\n        uint256 totalAmountPool; // total amount pool deposited (in LP tokens)\n        uint256 sumTaxesOverflow; // total taxes collected (starts at 0, increases with each harvest if overflow)\n        SaleType saleType; // previously bool checking if a sale is special(private), currently uint act as \"sale type\"\n        // 0: public sale\n        // 1: private sale\n        // 2: basic sale\n        VestingConfig vestingConfig;\n    }\n\n    // Array of PoolCharacteristics of size NUMBER_POOLS\n    PoolCharacteristics[2] private _poolInformation;\n\n    // Checks if user has claimed points\n    mapping(address => bool) private _hasClaimedPoints;\n\n    // Struct that contains each user information for both pools\n    struct UserInfo {\n        uint256 amountPool; // How many tokens the user has provided for pool\n        bool claimedPool; // Whether the user has claimed (default: false) for pool\n    }\n\n    // It maps the address to pool id to UserInfo\n    mapping(address => mapping(uint8 => UserInfo)) private _userInfo;\n\n    // It maps user address to credit used amount\n    mapping(address => uint256) public userCreditUsed;\n\n    // It maps if nft token id was used\n    mapping(uint256 => address) public tokenIdUsed;\n\n    // It maps user address with NFT id\n    mapping(address => uint256) public userNftTokenId;\n\n    // vesting startTime, everyone will be started at same timestamp\n    uint256 public vestingStartTime;\n\n    // A flag for vesting is being revoked\n    bool public vestingRevoked;\n\n    // Struct that contains vesting schedule\n    struct VestingSchedule {\n        bool isVestingInitialized;\n        // beneficiary of tokens after they are released\n        address beneficiary;\n        // pool id\n        uint8 pid;\n        // total amount of tokens to be released at the end of the vesting\n        uint256 amountTotal;\n        // amount of tokens has been released\n        uint256 released;\n    }\n\n    bytes32[] private vestingSchedulesIds;\n    mapping(bytes32 => VestingSchedule) private vestingSchedules;\n    uint256 private vestingSchedulesTotalAmount;\n    mapping(address => uint256) private holdersVestingCount;\n\n    // Admin withdraw events\n    event AdminWithdraw(uint256 amountLP, uint256 amountOfferingToken);\n\n    // Admin recovers token\n    event AdminTokenRecovery(address tokenAddress, uint256 amountTokens);\n\n    // Deposit event\n    event Deposit(address indexed user, uint256 amount, uint8 indexed pid);\n\n    // Harvest event\n    event Harvest(address indexed user, uint256 offeringAmount, uint256 excessAmount, uint8 indexed pid);\n\n    // Create VestingSchedule event\n    event CreateVestingSchedule(address indexed user, uint256 offeringAmount, uint256 excessAmount, uint8 indexed pid);\n\n    // Event for new start & end timestamps\n    event NewStartAndEndTimestamps(uint256 startTimestamp, uint256 endTimestamp);\n\n    // Event with point parameters for IFO\n    event PointParametersSet(uint256 campaignId, uint256 numberPoints, uint256 thresholdPoints);\n\n    // Event when parameters are set for one of the pools\n    event PoolParametersSet(uint256 offeringAmountPool, uint256 raisingAmountPool, uint8 pid);\n\n    // Event when released new amount\n    event Released(address indexed beneficiary, uint256 amount);\n\n    // Event when revoked\n    event Revoked();\n\n    error PoolIdNotValid();\n    error TokensNotDepositedProperly();\n    error NotEnoughIFOCreditLeft();\n    error NewAmountAboveUserLimit();\n    error ProfileNotActive();\n    error NotMeetAnyoneOfRequiredConditions();\n    error NFTRequirementsMustBeMetForHarvest();\n    error NFTUsedByAnotherAddressAlready();\n    error NFTTokenIdNotSameAsRegistered();\n    error CanNotBeLPToken();\n    error CanNotBeOfferingToken();\n    error VestingOnlyBeneficiaryOrOwnerCanRelease();\n    error VestingNotEnoughToRelease();\n    error VestingIsRevoked();\n    error OnlyOwner();\n\n    /**\n     * @notice Constructor\n     */\n    constructor() public {\n        IFO_FACTORY = msg.sender;\n    }\n\n    /**\n     * @notice It initializes the contract\n     * @dev It can only be called once.\n     * @param _addresses: [0] lpToken [1] offeringToken [2] pancakeProfile [3] iCake [4] adminAddress [5] admissionProfile\n     * @param _startAndEndTimestamps: [0] startTimestamp [1] endTimestamp\n     * @param _maxBufferSeconds: maximum buffer of blocks from the current block number\n     * @param _maxPoolId: maximum id of pools, sometimes only public sale exist\n     * @param _pointThreshold: threshold of user's point in pancake profile\n     * @param _vestingStartTime: the start timestamp of vesting\n     */\n    function initialize(\n        address[] calldata _addresses,\n        uint256[] calldata _startAndEndTimestamps,\n        uint256 _maxBufferSeconds,\n        uint8 _maxPoolId,\n        uint256 _pointThreshold,\n        uint256 _vestingStartTime\n    ) public {\n        // Check validation\n        IFOLibV8.InitializePreCheck(\n            isInitialized,\n            IFO_FACTORY,\n            _addresses.length,\n            _startAndEndTimestamps.length,\n            _maxPoolId\n        );\n\n        // Make this contract initialized\n        isInitialized = true;\n\n        if (_addresses[2] != address(0)) {\n            IPancakeProfile(_addresses[2]).getTeamProfile(1);\n        }\n\n        if (_addresses[3] != address(0)) {\n            ICakeV3(_addresses[3]).admin();\n        }\n\n        // [0] lpToken\n        // [1] offeringToken\n        // [2] pancakeProfile\n        // [3] iCake\n        // [4] adminAddress\n        // [5] admissionProfile\n        for (uint8 i = 0; i < _addresses.length; i++) {\n            addresses[i] = _addresses[i];\n        }\n\n        startTimestamp = _startAndEndTimestamps[0];\n        endTimestamp = _startAndEndTimestamps[1];\n        MAX_BUFFER_SECONDS = _maxBufferSeconds;\n        MAX_POOL_ID = _maxPoolId;\n        pointThreshold = _pointThreshold;\n        vestingStartTime = _vestingStartTime;\n\n        // Transfer ownership to admin\n        transferOwnership(_addresses[4]);\n    }\n\n    /**\n     * @notice It allows users to deposit LP tokens to pool\n     * @param _amount: the number of LP token used (18 decimals)\n     * @param _pid: pool id\n     */\n    function depositPool(uint256 _amount, uint8 _pid) external override nonReentrant {\n        // Checks whether the pool id is valid\n        _checkPid(_pid);\n\n        // Check validation\n        IFOLibV8.DepositPoolPreCheck(\n            _amount,\n            addresses[2], // pancakeProfileAddress\n            _poolInformation[_pid].saleType,\n            _poolInformation[_pid].offeringAmountPool,\n            _poolInformation[_pid].raisingAmountPool,\n            startTimestamp,\n            endTimestamp\n        );\n\n        // Verify tokens were deposited properly\n        if (IERC20(addresses[1]).balanceOf(address(this)) < totalTokensOffered) {\n            revert TokensNotDepositedProperly();\n        }\n\n        if (_poolInformation[_pid].saleType == SaleType.PUBLIC || _poolInformation[_pid].saleType == SaleType.BASIC) {\n            // public and basic sales\n            if (addresses[3] != address(0) && _poolInformation[_pid].saleType != SaleType.BASIC) {\n                // getUserCredit from ICake contract when it is presented and not basic sales\n                uint256 ifoCredit = ICakeV3(addresses[3]).getUserCredit(msg.sender);\n\n                if (userCreditUsed[msg.sender] + _amount > ifoCredit) {\n                    revert NotEnoughIFOCreditLeft();\n                }\n            }\n\n            _deposit(_amount, _pid);\n\n            // Updates Accumulative deposit lpTokens\n            userCreditUsed[msg.sender] = userCreditUsed[msg.sender] + (_poolInformation[_pid].saleType == SaleType.PUBLIC ? _amount : 0);\n        } else {\n            // private sales\n            if (addresses[2] != address(0)) {\n                (\n                    ,\n                    uint256 profileNumberPoints,\n                    ,\n                    address profileAddress,\n                    uint256 tokenId,\n                    bool active\n                ) = IPancakeProfile(addresses[2]).getUserProfile(msg.sender);\n\n                if (!active) revert ProfileNotActive();\n\n                if (!_isQualifiedPoints(profileNumberPoints) &&\n                    !isQualifiedWhitelist(msg.sender) &&\n                    !_isQualifiedNFT(msg.sender, profileAddress, tokenId)) {\n                    revert NotMeetAnyoneOfRequiredConditions();\n                }\n\n                // Update tokenIdUsed\n                if (!_isQualifiedPoints(profileNumberPoints) &&\n                    !isQualifiedWhitelist(msg.sender) &&\n                    profileAddress == addresses[5]) {\n                    if (tokenIdUsed[tokenId] == address(0)) {\n                        // update tokenIdUsed\n                        tokenIdUsed[tokenId] = msg.sender;\n                    } else {\n                        if (tokenIdUsed[tokenId] != msg.sender) {\n                            revert NFTUsedByAnotherAddressAlready();\n                        }\n                    }\n                    if (userNftTokenId[msg.sender] == 0) {\n                        // update userNftTokenId\n                        userNftTokenId[msg.sender] = tokenId;\n                    } else {\n                        if (userNftTokenId[msg.sender] != tokenId) {\n                            revert NFTTokenIdNotSameAsRegistered();\n                        }\n                    }\n                }\n            }\n\n            _deposit(_amount, _pid);\n        }\n    }\n\n    /**\n     * @notice It allows users to harvest from pool\n     * @param _pid: pool id\n     */\n    function harvestPool(uint8 _pid) external override nonReentrant {\n        // Checks whether the pool id is valid\n        _checkPid(_pid);\n\n        // Check validation\n        IFOLibV8.HarvestPoolPreCheck(\n            endTimestamp,\n            _userInfo[msg.sender][_pid].amountPool,\n            _userInfo[msg.sender][_pid].claimedPool\n        );\n\n        if (userNftTokenId[msg.sender] != 0) {\n            (, , , address profileAddress, uint256 tokenId, bool isActive) = IPancakeProfile(addresses[2])\n                .getUserProfile(msg.sender);\n\n            if (!isActive || profileAddress != addresses[5] || userNftTokenId[msg.sender] != tokenId) {\n                revert NFTRequirementsMustBeMetForHarvest();\n            }\n        }\n\n        // Claim points if possible\n        _claimPoints(msg.sender);\n\n        // Updates the harvest status\n        _userInfo[msg.sender][_pid].claimedPool = true;\n\n        // Updates the vesting startTime\n        if (vestingStartTime == 0) {\n            vestingStartTime = block.timestamp;\n        }\n\n        // Initialize the variables for offering, refunding user amounts, and tax amount\n        (\n            uint256 offeringTokenAmount,\n            uint256 refundingTokenAmount,\n            uint256 userTaxOverflow\n        ) = _calculateOfferingAndRefundingAmountsPool(msg.sender, _pid);\n\n        // Increment the sumTaxesOverflow\n        if (userTaxOverflow > 0) {\n            _poolInformation[_pid].sumTaxesOverflow = _poolInformation[_pid].sumTaxesOverflow + userTaxOverflow;\n        }\n\n        // Transfer these tokens back to the user if quantity > 0\n        if (offeringTokenAmount > 0) {\n            if (100 - _poolInformation[_pid].vestingConfig.percentage > 0) {\n                uint256 amount = offeringTokenAmount * (100 - _poolInformation[_pid].vestingConfig.percentage) / 100;\n\n                // Transfer the tokens at TGE\n                IERC20(addresses[1]).safeTransfer(msg.sender, amount);\n\n                emit Harvest(msg.sender, amount, refundingTokenAmount, _pid);\n            }\n            // If this pool is Vesting modal, create a VestingSchedule for each user\n            if (_poolInformation[_pid].vestingConfig.percentage > 0) {\n                uint256 amount = offeringTokenAmount * _poolInformation[_pid].vestingConfig.percentage / 100;\n\n                // Create VestingSchedule object\n                _createVestingSchedule(msg.sender, _pid, amount);\n\n                emit CreateVestingSchedule(msg.sender, amount, refundingTokenAmount, _pid);\n            }\n        }\n\n        if (refundingTokenAmount > 0) {\n            IERC20(addresses[0]).safeTransfer(msg.sender, refundingTokenAmount);\n        }\n    }\n\n    /**\n     * @notice It allows the admin to withdraw funds\n     * @param _lpAmount: the number of LP token to withdraw (18 decimals)\n     * @param _offerAmount: the number of offering amount to withdraw\n     * @dev This function is only callable by admin.\n     */\n    function finalWithdraw(uint256 _lpAmount, uint256 _offerAmount) external override {\n        _isOwner();\n        // Check validation\n        IFOLibV8.FinalWithdrawPreCheck(\n            _lpAmount,\n            IERC20(addresses[0]).balanceOf(address(this)),\n            _offerAmount,\n            IERC20(addresses[1]).balanceOf(address(this))\n        );\n\n        if (_lpAmount > 0) {\n            IERC20(addresses[0]).safeTransfer(msg.sender, _lpAmount);\n        }\n\n        if (_offerAmount > 0) {\n            IERC20(addresses[1]).safeTransfer(msg.sender, _offerAmount);\n        }\n\n        emit AdminWithdraw(_lpAmount, _offerAmount);\n    }\n\n    /**\n     * @notice It allows the admin to recover wrong tokens sent to the contract\n     * @param _tokenAddress: the address of the token to withdraw (18 decimals)\n     * @param _tokenAmount: the number of token amount to withdraw\n     * @dev This function is only callable by admin.\n     */\n    function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external {\n        _isOwner();\n\n        if (_tokenAddress == addresses[0]) {\n            revert CanNotBeLPToken();\n        }\n        if (_tokenAddress == addresses[1]) {\n            revert CanNotBeOfferingToken();\n        }\n\n        IERC20(_tokenAddress).safeTransfer(msg.sender, _tokenAmount);\n\n        emit AdminTokenRecovery(_tokenAddress, _tokenAmount);\n    }\n\n    /**\n     * @notice It sets parameters for pool\n     * @param _offeringAmountPool: offering amount (in tokens)\n     * @param _raisingAmountPool: raising amount (in LP tokens)\n     * @param _limitPerUserInLP: limit per user (in LP tokens)\n     * @param _hasTax: if the pool has a tax\n     * @param _flatTaxRate: flat tax rate\n     * @param _pid: pool id\n     * @param _saleType: // previously bool checking if a sale is special(private), currently uint act as \"sale type\"\n        // 0: public sale\n        // 1: private sale\n        // 2: basic sale\n     * @param _vestingConfig: vesting config parameters\n     * @dev This function is only callable by admin.\n     */\n    function setPool(\n        uint256 _offeringAmountPool,\n        uint256 _raisingAmountPool,\n        uint256 _limitPerUserInLP,\n        bool _hasTax,\n        uint256 _flatTaxRate,\n        uint8 _pid,\n        SaleType _saleType,\n        VestingConfig calldata _vestingConfig\n    ) external override {\n        _isOwner();\n\n        // Checks whether the pool id is valid\n        _checkPid(_pid);\n\n        // Check validation\n        IFOLibV8.SetPoolPreCheck(\n            startTimestamp,\n            _hasTax,\n            _flatTaxRate,\n            _vestingConfig.percentage,\n            _vestingConfig.duration,\n            _vestingConfig.slicePeriodSeconds\n        );\n\n        _poolInformation[_pid].offeringAmountPool = _offeringAmountPool;\n        _poolInformation[_pid].raisingAmountPool = _raisingAmountPool;\n        _poolInformation[_pid].limitPerUserInLP = _limitPerUserInLP;\n        _poolInformation[_pid].hasTax = _hasTax;\n        _poolInformation[_pid].flatTaxRate = _flatTaxRate;\n        _poolInformation[_pid].saleType = _saleType;\n        _poolInformation[_pid].vestingConfig.percentage = _vestingConfig.percentage;\n        _poolInformation[_pid].vestingConfig.cliff = _vestingConfig.cliff;\n        _poolInformation[_pid].vestingConfig.duration = _vestingConfig.duration;\n        _poolInformation[_pid].vestingConfig.slicePeriodSeconds = _vestingConfig.slicePeriodSeconds;\n\n        uint256 tokensDistributedAcrossPools;\n\n        for (uint8 i = 0; i <= MAX_POOL_ID; i++) {\n            tokensDistributedAcrossPools = tokensDistributedAcrossPools + _poolInformation[i].offeringAmountPool;\n        }\n\n        // Update totalTokensOffered\n        totalTokensOffered = tokensDistributedAcrossPools;\n\n        emit PoolParametersSet(_offeringAmountPool, _raisingAmountPool, _pid);\n    }\n\n    /**\n     * @notice It updates point parameters for the IFO.\n     * @param _pointConfig: the point reward and requirement for user participate in IFO\n     * @dev This function is only callable by admin.\n     */\n    function updatePointParameters(\n        PointConfig calldata _pointConfig\n    ) external override {\n        _isOwner();\n\n        // Check validation\n        IFOLibV8.UpdatePointParametersPreCheck(\n            endTimestamp\n        );\n\n        pointConfig.numberPoints = _pointConfig.numberPoints;\n        pointConfig.campaignId = _pointConfig.campaignId;\n        pointConfig.thresholdPoints = _pointConfig.thresholdPoints;\n\n        emit PointParametersSet(_pointConfig.campaignId, _pointConfig.numberPoints, _pointConfig.thresholdPoints);\n    }\n\n    /**\n     * @notice It allows the admin to update start and end blocks\n     * @param _startAndEndTimestamps: [0] startTimestamp [1] endTimestamp\n     * @dev This function is only callable by admin.\n     */\n    function updateStartAndEndTimestamps(uint256[] calldata _startAndEndTimestamps) external {\n        _isOwner();\n\n        // Check validation\n        IFOLibV8.UpdateStartAndEndTimestampsPreCheck(\n            MAX_BUFFER_SECONDS,\n            _startAndEndTimestamps.length,\n            startTimestamp,\n            _startAndEndTimestamps[0], // startTimestamp\n            _startAndEndTimestamps[1]  // endTimestamp\n        );\n\n        startTimestamp = _startAndEndTimestamps[0];\n        endTimestamp = _startAndEndTimestamps[1];\n\n        emit NewStartAndEndTimestamps(_startAndEndTimestamps[0], _startAndEndTimestamps[1]);\n    }\n\n    /**\n     * @notice It returns the pool information\n     * @param _pid: pool id\n     * @return raisingAmountPool: amount of LP tokens raised (in LP tokens)\n     * @return offeringAmountPool: amount of tokens offered for the pool (in offeringTokens)\n     * @return limitPerUserInLP; // limit of tokens per user (if 0, it is ignored)\n     * @return hasTax: tax on the overflow (if any, it works with _calculateTaxOverflow)\n     * @return flatTaxRate: new rate of flat tax\n     * @return totalAmountPool: total amount pool deposited (in LP tokens)\n     * @return sumTaxesOverflow: total taxes collected (starts at 0, increases with each harvest if overflow)\n     */\n    function viewPoolInformation(uint256 _pid)\n    external\n    view\n    returns (\n        uint256,\n        uint256,\n        uint256,\n        bool,\n        uint256,\n        uint256,\n        SaleType\n    ) {\n        return (\n            _poolInformation[_pid].raisingAmountPool,\n            _poolInformation[_pid].offeringAmountPool,\n            _poolInformation[_pid].limitPerUserInLP,\n            _poolInformation[_pid].hasTax,\n            _poolInformation[_pid].totalAmountPool,\n            _poolInformation[_pid].sumTaxesOverflow,\n            _poolInformation[_pid].saleType\n        );\n    }\n\n    /**\n     * @notice It returns the pool vesting information\n     * @param _pid: pool id\n     * @return vestingPercentage: the percentage of vesting part, claimingPercentage + vestingPercentage should be 100\n     * @return vestingCliff: the cliff of vesting\n     * @return vestingDuration: the duration of vesting\n     * @return vestingSlicePeriodSeconds: the slice period seconds of vesting\n     */\n    function viewPoolVestingInformation(uint256 _pid)\n    external\n    view\n    override\n    returns (\n        uint256,\n        uint256,\n        uint256,\n        uint256\n    )\n    {\n        return (\n            _poolInformation[_pid].vestingConfig.percentage,\n            _poolInformation[_pid].vestingConfig.cliff,\n            _poolInformation[_pid].vestingConfig.duration,\n            _poolInformation[_pid].vestingConfig.slicePeriodSeconds\n        );\n    }\n\n    /**\n     * @notice It returns the tax overflow rate calculated for a pool\n     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)\n     * @param _pid: pool id\n     * @return It returns the tax percentage\n     */\n    function viewPoolTaxRateOverflow(uint256 _pid) external view returns (uint256) {\n        if (!_poolInformation[_pid].hasTax) {\n            return 0;\n        } else {\n            if (_poolInformation[_pid].flatTaxRate > 0) {\n                return _poolInformation[_pid].flatTaxRate;\n            } else {\n                return\n                    _calculateTaxOverflow(\n                    _poolInformation[_pid].totalAmountPool,\n                    _poolInformation[_pid].raisingAmountPool\n                );\n            }\n        }\n    }\n\n    /**\n     * @notice External view function to see user allocations for both pools\n     * @param _user: user address\n     * @param _pids[]: array of pids\n     * @return\n     */\n    function viewUserAllocationPools(address _user, uint8[] calldata _pids) external view returns (uint256[] memory) {\n        uint256[] memory allocationPools = new uint256[](_pids.length);\n        for (uint8 i = 0; i < _pids.length; i++) {\n            allocationPools[i] = _getUserAllocationPool(_user, _pids[i]);\n        }\n        return allocationPools;\n    }\n\n    /**\n     * @notice External view function to see user information\n     * @param _user: user address\n     * @param _pids[]: array of pids\n     */\n    function viewUserInfo(address _user, uint8[] calldata _pids)\n    external\n    view\n    returns (uint256[] memory, bool[] memory) {\n        uint256[] memory amountPools = new uint256[](_pids.length);\n        bool[] memory statusPools = new bool[](_pids.length);\n\n        for (uint8 i = 0; i <= MAX_POOL_ID; i++) {\n            amountPools[i] = _userInfo[_user][i].amountPool;\n            statusPools[i] = _userInfo[_user][i].claimedPool;\n        }\n        return (amountPools, statusPools);\n    }\n\n    /**\n     * @notice External view function to see user offering and refunding amounts for both pools\n     * @param _user: user address\n     * @param _pids: array of pids\n     */\n    function viewUserOfferingAndRefundingAmountsForPools(address _user, uint8[] calldata _pids)\n    external\n    view\n    returns (uint256[3][] memory) {\n        uint256[3][] memory amountPools = new uint256[3][](_pids.length);\n\n        for (uint8 i = 0; i < _pids.length; i++) {\n            uint256 userOfferingAmountPool;\n            uint256 userRefundingAmountPool;\n            uint256 userTaxAmountPool;\n\n            if (_poolInformation[_pids[i]].raisingAmountPool > 0) {\n                (\n                    userOfferingAmountPool,\n                    userRefundingAmountPool,\n                    userTaxAmountPool\n                ) = _calculateOfferingAndRefundingAmountsPool(_user, _pids[i]);\n            }\n\n            amountPools[i] = [userOfferingAmountPool, userRefundingAmountPool, userTaxAmountPool];\n        }\n        return amountPools;\n    }\n\n    /**\n     * @notice Returns the vesting schedule information of a given holder and index\n     * @return The vesting schedule object\n     */\n    function getVestingScheduleByAddressAndIndex(address _holder, uint256 _index)\n    external\n    view\n    returns (VestingSchedule memory)\n    {\n        return getVestingSchedule(computeVestingScheduleIdForAddressAndIndex(_holder, _index));\n    }\n\n    /**\n     * @notice Returns the total amount of vesting schedules\n     * @return The vesting schedule total amount\n     */\n    function getVestingSchedulesTotalAmount() external view returns (uint256) {\n        return vestingSchedulesTotalAmount;\n    }\n\n    /**\n     * @notice Release vested amount of offering tokens\n     * @param _vestingScheduleId the vesting schedule identifier\n     */\n    function release(bytes32 _vestingScheduleId) external nonReentrant {\n        // Check validation\n        IFOLibV8.IsVestingInitializedPreCheck(\n            vestingSchedules[_vestingScheduleId].isVestingInitialized\n        );\n\n        VestingSchedule storage vestingSchedule = vestingSchedules[_vestingScheduleId];\n        bool isBeneficiary = msg.sender == vestingSchedule.beneficiary;\n        bool isOwner = msg.sender == owner();\n        if (!isBeneficiary && !isOwner) {\n            revert VestingOnlyBeneficiaryOrOwnerCanRelease();\n        }\n        uint256 vestedAmount = _computeReleasableAmount(vestingSchedule);\n        if (vestedAmount <= 0) {\n            revert VestingNotEnoughToRelease();\n        }\n        vestingSchedule.released = vestingSchedule.released + vestedAmount;\n        vestingSchedulesTotalAmount = vestingSchedulesTotalAmount - vestedAmount;\n        IERC20(addresses[1]).safeTransfer(vestingSchedule.beneficiary, vestedAmount);\n\n        emit Released(vestingSchedule.beneficiary, vestedAmount);\n    }\n\n    /**\n     * @notice Revokes all the vesting schedules\n     */\n    function revoke() external {\n        _isOwner();\n\n        if (vestingRevoked) {\n            revert VestingIsRevoked();\n        }\n\n        vestingRevoked = true;\n        emit Revoked();\n    }\n\n    /**\n     * @notice Returns the number of vesting schedules managed by the contract\n     * @return The number of vesting count\n     */\n    function getVestingSchedulesCount() public view returns (uint256) {\n        return vestingSchedulesIds.length;\n    }\n\n    /**\n     * @notice Returns the vested amount of tokens for the given vesting schedule identifier\n     * @return The number of vested count\n     */\n    function computeReleasableAmount(bytes32 _vestingScheduleId) public view returns (uint256) {\n        // Check validation\n        IFOLibV8.IsVestingInitializedPreCheck(\n            vestingSchedules[_vestingScheduleId].isVestingInitialized\n        );\n\n        return _computeReleasableAmount(vestingSchedules[_vestingScheduleId]);\n    }\n\n    /**\n     * @notice Returns the vesting schedule information of a given identifier\n     * @return The vesting schedule object\n     */\n    function getVestingSchedule(bytes32 _vestingScheduleId) public view returns (VestingSchedule memory) {\n        return vestingSchedules[_vestingScheduleId];\n    }\n\n    /**\n     * @notice Returns the amount of offering token that can be withdrawn by the owner\n     * @return The amount of offering token\n     */\n    function getWithdrawableOfferingTokenAmount() public view returns (uint256) {\n        return IERC20(addresses[1]).balanceOf(address(this)) - vestingSchedulesTotalAmount;\n    }\n\n    /**\n     * @notice Computes the next vesting schedule identifier for a given holder address\n     * @return The id string\n     */\n    function computeNextVestingScheduleIdForHolder(address _holder) public view returns (bytes32) {\n        return computeVestingScheduleIdForAddressAndIndex(_holder, holdersVestingCount[_holder]);\n    }\n\n    /**\n     * @notice Computes the next vesting schedule identifier for an address and an index\n     * @return The id string\n     */\n    function computeVestingScheduleIdForAddressAndIndex(address _holder, uint256 _index) public pure returns (bytes32) {\n        return keccak256(abi.encodePacked(_holder, _index));\n    }\n\n    /**\n     * @notice Computes the next vesting schedule identifier for an address and an pid\n     * @return The id string\n     */\n    function computeVestingScheduleIdForAddressAndPid(address _holder, uint8 _pid) external view returns (bytes32) {\n        // Checks whether the pool id is valid\n        _checkPid(_pid);\n\n        bytes32 vestingScheduleId = computeVestingScheduleIdForAddressAndIndex(_holder, 0);\n        VestingSchedule memory vestingSchedule = vestingSchedules[vestingScheduleId];\n        if (vestingSchedule.pid == _pid) {\n            return vestingScheduleId;\n        } else {\n            return computeVestingScheduleIdForAddressAndIndex(_holder, 1);\n        }\n    }\n\n    /**\n     * @notice Computes the releasable amount of tokens for a vesting schedule\n     * @return The amount of releasable tokens\n     */\n    function _computeReleasableAmount(VestingSchedule memory _vestingSchedule) internal view returns (uint256) {\n        if (block.timestamp < vestingStartTime + _poolInformation[_vestingSchedule.pid].vestingConfig.cliff) {\n            return 0;\n        } else if (\n            block.timestamp >= vestingStartTime + _poolInformation[_vestingSchedule.pid].vestingConfig.duration ||\n            vestingRevoked\n        ) {\n            return _vestingSchedule.amountTotal - _vestingSchedule.released;\n        } else {\n            uint256 timeFromStart = block.timestamp - vestingStartTime;\n            uint256 secondsPerSlice = _poolInformation[_vestingSchedule.pid].vestingConfig.slicePeriodSeconds;\n            uint256 vestedSlicePeriods = timeFromStart / secondsPerSlice;\n            uint256 vestedSeconds = vestedSlicePeriods * secondsPerSlice;\n            uint256 vestedAmount = _vestingSchedule.amountTotal * vestedSeconds / _poolInformation[_vestingSchedule.pid].vestingConfig.duration;\n            vestedAmount = vestedAmount - _vestingSchedule.released;\n            return vestedAmount;\n        }\n    }\n\n    /**\n     * @notice Creates a new vesting schedule for a beneficiary\n     * @param _beneficiary address of the beneficiary to whom vested tokens are transferred\n     * @param _pid the pool id\n     * @param _amount total amount of tokens to be released at the end of the vesting\n     */\n    function _createVestingSchedule(\n        address _beneficiary,\n        uint8 _pid,\n        uint256 _amount\n    ) internal {\n        require(\n            getWithdrawableOfferingTokenAmount() >= _amount,\n            \"can not create vesting schedule with sufficient tokens\"\n        );\n\n        bytes32 vestingScheduleId = computeNextVestingScheduleIdForHolder(_beneficiary);\n        require(vestingSchedules[vestingScheduleId].beneficiary == address(0), \"vestingScheduleId is been created\");\n        vestingSchedules[vestingScheduleId] = VestingSchedule(true, _beneficiary, _pid, _amount, 0);\n        vestingSchedulesTotalAmount = vestingSchedulesTotalAmount + _amount;\n        vestingSchedulesIds.push(vestingScheduleId);\n        holdersVestingCount[_beneficiary]++;\n    }\n\n    /**\n     * @notice It allows users to claim points\n     * @param _user: user address\n     */\n    function _claimPoints(address _user) internal {\n        if (addresses[2] != address(0)) {\n            if (!_hasClaimedPoints[_user] && pointConfig.numberPoints > 0) {\n                uint256 sumPools;\n                for (uint8 i = 0; i <= MAX_POOL_ID; i++) {\n                    sumPools = sumPools + _userInfo[msg.sender][i].amountPool;\n                }\n                if (sumPools > pointConfig.thresholdPoints) {\n                    _hasClaimedPoints[_user] = true;\n                    // Increase user points\n                    IPancakeProfile(addresses[2]).increaseUserPoints(msg.sender, pointConfig.numberPoints, pointConfig.campaignId);\n                }\n            }\n        }\n    }\n\n    /**\n     * @notice It calculates the tax overflow given the raisingAmountPool and the totalAmountPool.\n     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)\n     * @return It returns the tax percentage\n     */\n    function _calculateTaxOverflow(uint256 _totalAmountPool, uint256 _raisingAmountPool)\n    internal\n    pure\n    returns (uint256)\n    {\n        uint256 ratioOverflow = _totalAmountPool / _raisingAmountPool;\n        if (ratioOverflow >= 1500) {\n            return 250000000; // 0.0125%\n        } else if (ratioOverflow >= 1000) {\n            return 500000000; // 0.05%\n        } else if (ratioOverflow >= 500) {\n            return 1000000000; // 0.1%\n        } else if (ratioOverflow >= 250) {\n            return 1250000000; // 0.125%\n        } else if (ratioOverflow >= 100) {\n            return 1500000000; // 0.15%\n        } else if (ratioOverflow >= 50) {\n            return 2500000000; // 0.25%\n        } else {\n            return 5000000000; // 0.5%\n        }\n    }\n\n    /**\n     * @notice It calculates the offering amount for a user and the number of LP tokens to transfer back.\n     * @param _user: user address\n     * @param _pid: pool id\n     * @return {uint256, uint256, uint256} It returns the offering amount, the refunding amount (in LP tokens),\n     * and the tax (if any, else 0)\n     */\n    function _calculateOfferingAndRefundingAmountsPool(address _user, uint8 _pid)\n    internal\n    view\n    returns (\n        uint256,\n        uint256,\n        uint256\n    )\n    {\n        uint256 userOfferingAmount;\n        uint256 userRefundingAmount;\n        uint256 taxAmount;\n\n        if (_poolInformation[_pid].totalAmountPool > _poolInformation[_pid].raisingAmountPool) {\n            // Calculate allocation for the user\n            uint256 allocation = _getUserAllocationPool(_user, _pid);\n\n            // Calculate the offering amount for the user based on the offeringAmount for the pool\n            userOfferingAmount = _poolInformation[_pid].offeringAmountPool * allocation / 1e12;\n\n            // Calculate the payAmount\n            uint256 payAmount = _poolInformation[_pid].raisingAmountPool * allocation / 1e12;\n\n            // Calculate the pre-tax refunding amount\n            userRefundingAmount = _userInfo[_user][_pid].amountPool - payAmount;\n\n            // Retrieve the tax rate\n            if (_poolInformation[_pid].hasTax) {\n                uint256 tax = _poolInformation[_pid].flatTaxRate;\n\n                if (tax == 0) {\n                    tax = _calculateTaxOverflow(\n                        _poolInformation[_pid].totalAmountPool,\n                        _poolInformation[_pid].raisingAmountPool\n                    );\n                }\n                // Calculate the final taxAmount\n                taxAmount = userRefundingAmount * tax / 1e12;\n\n                // Adjust the refunding amount\n                userRefundingAmount = userRefundingAmount - taxAmount;\n            }\n        } else {\n            // _userInfo[_user] / (raisingAmount / offeringAmount)\n            userOfferingAmount = _userInfo[_user][_pid].amountPool * _poolInformation[_pid].offeringAmountPool / _poolInformation[_pid].raisingAmountPool;\n        }\n        return (userOfferingAmount, userRefundingAmount, taxAmount);\n    }\n\n    /**\n     * @notice It returns the user allocation for pool\n     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)\n     * @param _user: user address\n     * @param _pid: pool id\n     * @return It returns the user's share of pool\n     */\n    function _getUserAllocationPool(address _user, uint8 _pid) internal view returns (uint256) {\n        if (_pid > MAX_POOL_ID) {\n            return 0;\n        }\n\n        if (_poolInformation[_pid].totalAmountPool > 0) {\n            return _userInfo[_user][_pid].amountPool * 1e12 / _poolInformation[_pid].totalAmountPool;\n        } else {\n            return 0;\n        }\n    }\n\n    function isQualifiedWhitelist(address _user) public view returns (bool) {\n        return isWhitelisted(_user);\n    }\n\n    function isQualifiedPoints(address _user) external view returns (bool) {\n        if (addresses[2] == address(0)) {\n            return true;\n        }\n        if (!IPancakeProfile(addresses[2]).getUserStatus(_user)) {\n            return false;\n        }\n\n        (, uint256 profileNumberPoints, , , , ) = IPancakeProfile(addresses[2]).getUserProfile(_user);\n        return _isQualifiedPoints(profileNumberPoints);\n    }\n\n    function isQualifiedNFT(address _user) external view returns (bool) {\n        if (addresses[2] == address(0)) {\n            return true;\n        }\n        if (!IPancakeProfile(addresses[2]).getUserStatus(_user)) {\n            return false;\n        }\n\n        (, , , address profileAddress, uint256 tokenId, ) = IPancakeProfile(addresses[2]).getUserProfile(\n            _user\n        );\n\n        return _isQualifiedNFT(_user, profileAddress, tokenId);\n    }\n\n    function _isQualifiedPoints(uint256 profileNumberPoints) internal view returns (bool) {\n        return (pointThreshold != 0 && profileNumberPoints >= pointThreshold);\n    }\n\n    function _isQualifiedNFT(\n        address _user,\n        address profileAddress,\n        uint256 tokenId\n    ) internal view returns (bool) {\n        return (profileAddress == addresses[5] &&\n            (tokenIdUsed[tokenId] == address(0) || tokenIdUsed[tokenId] == _user));\n    }\n\n    function _isOwner() internal view {\n        if (owner() != msg.sender) revert OnlyOwner();\n    }\n\n    function _deposit(uint256 _amount, uint8 _pid) internal {\n        // Transfers funds to this contract\n        IERC20(addresses[0]).safeTransferFrom(msg.sender, address(this), _amount);\n\n        // Update the user status\n        _userInfo[msg.sender][_pid].amountPool = _userInfo[msg.sender][_pid].amountPool + _amount;\n\n        // Check if the pool has a limit per user\n        if (_poolInformation[_pid].limitPerUserInLP > 0) {\n            // Checks whether the limit has been reached\n            if (_userInfo[msg.sender][_pid].amountPool > _poolInformation[_pid].limitPerUserInLP) {\n                revert NewAmountAboveUserLimit();\n            }\n        }\n\n        // Updates the totalAmount for pool\n        _poolInformation[_pid].totalAmountPool = _poolInformation[_pid].totalAmountPool + _amount;\n\n        emit Deposit(msg.sender, _amount, _pid);\n    }\n\n    function _checkPid(uint8 _pid) internal view {\n        if (_pid > MAX_POOL_ID) {\n            revert PoolIdNotValid();\n        }\n    }\n}"
      },
      "contracts/interfaces/IIFOV8.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\n/** @title IIFOV8.\n * @notice It is an interface for IFOV8.sol\n */\ninterface IIFOV8 {\n    enum SaleType {\n        PUBLIC, //0\n        PRIVATE, //1\n        BASIC //2\n    }\n\n    struct VestingConfig {\n        uint256 percentage;\n        uint256 cliff;\n        uint256 duration;\n        uint256 slicePeriodSeconds;\n    }\n\n    struct PointConfig {\n        uint256 campaignId;\n        uint256 numberPoints;\n        uint256 thresholdPoints;\n    }\n\n    function depositPool(uint256 _amount, uint8 _pid) external;\n\n    function harvestPool(uint8 _pid) external;\n\n    function finalWithdraw(uint256 _lpAmount, uint256 _offerAmount) external;\n\n    function setPool(\n        uint256 _offeringAmountPool,\n        uint256 _raisingAmountPool,\n        uint256 _limitPerUserInLP,\n        bool _hasTax,\n        uint256 _flatTaxRate,\n        uint8 _pid,\n        SaleType _saleType,\n        VestingConfig memory _vestingConfig\n    ) external;\n\n    function updatePointParameters(\n        PointConfig memory _pointConfig\n    ) external;\n\n    function viewPoolInformation(uint256 _pid)\n    external\n    view\n    returns (\n        uint256,\n        uint256,\n        uint256,\n        bool,\n        uint256,\n        uint256,\n        SaleType\n    );\n\n    function viewPoolVestingInformation(uint256 _pid)\n    external\n    view\n    returns (\n        uint256,\n        uint256,\n        uint256,\n        uint256\n    );\n\n    function viewPoolTaxRateOverflow(uint256 _pid) external view returns (uint256);\n\n    function viewUserAllocationPools(address _user, uint8[] calldata _pids) external view returns (uint256[] memory);\n\n    function viewUserInfo(address _user, uint8[] calldata _pids)\n    external\n    view\n    returns (uint256[] memory, bool[] memory);\n\n    function viewUserOfferingAndRefundingAmountsForPools(address _user, uint8[] calldata _pids)\n    external\n    view\n    returns (uint256[3][] memory);\n}"
      },
      "contracts/interfaces/IPancakeProfile.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\npragma experimental ABIEncoderV2;\n\ninterface IPancakeProfile {\n    /**\n     * @dev Check the user's profile for a given address\n     */\n    function getUserProfile(address _userAddress)\n        external\n        view\n        returns (\n            uint256,\n            uint256,\n            uint256,\n            address,\n            uint256,\n            bool\n        );\n\n    /**\n     * @dev Check the user's status for a given address\n     */\n    function getUserStatus(address _userAddress) external view returns (bool);\n\n    function getTeamProfile(uint256 _teamId)\n    external\n    view\n    returns (\n        string memory,\n        string memory,\n        uint256,\n        uint256,\n        bool\n    );\n\n    /**\n     * @dev To increase the number of points for a user.\n     * Callable only by point admins\n     */\n    function increaseUserPoints(\n        address _userAddress,\n        uint256 _numberPoints,\n        uint256 _campaignId\n    ) external;\n}\n\n"
      },
      "contracts/libraries/IFOLibV8.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport \"../interfaces/IIFOV8.sol\";\nimport \"../interfaces/IPancakeProfile.sol\";\nimport \"../ICakeV3.sol\";\n\nlibrary IFOLibV8 {\n\n    error PoolIdNotValid();\n    error EndTimeTooFar();\n    error StartTimeMustInferiorToEndTime();\n    error StartTimeMustGreaterThanCurrentBlockTime();\n    error AlreadyInitialized();\n    error NotFactory();\n    error AddressesLengthNotCorrect();\n    error StartAndEndTimestampsLengthNotCorrect();\n    error ShouldNotLargerThanTheNumberOfPools();\n    error MustHaveAnActiveProfile();\n    error PoolNotSet();\n    error TooEarly();\n    error TooLate();\n    error AmountMustExceedZero();\n    error DidNotParticipate();\n    error AlreadyHarvested();\n    error NotEnoughLPTokens();\n    error NotEnoughOfferingTokens();\n    error IFOHasStarted();\n    error IFOHasEnded();\n    error FlatTaxRateMustBeLessThan1e12();\n    error FlatTaxRateMustBe0WhenHasTaxIsFalse();\n    error VestingPercentageShouldRangeIn0And100();\n    error VestingDurationMustExceeds0();\n    error VestingSlicePerSecondsMustBeExceeds1();\n    error VestingSlicePerSecondsMustBeInteriorDuration();\n    error VestingNotExist();\n\n    function InitializePreCheck(\n        bool isInitialized,\n        address IFO_FACTORY,\n        uint256 addresses_length,\n        uint256 startAndEndTimestamps_length,\n        uint8 maxPoolId\n    ) internal view {\n        if (isInitialized) {\n            revert AlreadyInitialized();\n        }\n\n        if (msg.sender != IFO_FACTORY) {\n            revert NotFactory();\n        }\n\n        if (addresses_length != 6) {\n            revert AddressesLengthNotCorrect();\n        }\n\n        if (startAndEndTimestamps_length != 2) {\n            revert StartAndEndTimestampsLengthNotCorrect();\n        }\n\n        if (maxPoolId >= 2) {\n            revert ShouldNotLargerThanTheNumberOfPools();\n        }\n    }\n\n    function DepositPoolPreCheck(\n        uint256 amount,\n        address pancakeProfileAddress,\n        IIFOV8.SaleType saleType,\n        uint256 offeringAmountPool,\n        uint256 raisingAmountPool,\n        uint256 startTimestamp,\n        uint256 endTimestamp\n    ) internal view {\n        if (pancakeProfileAddress != address(0) && saleType != IIFOV8.SaleType.BASIC) {\n            // Checks whether the user has an active profile when provided profile SC and not basic sale\n            if (!IPancakeProfile(pancakeProfileAddress).getUserStatus(msg.sender)) {\n                revert MustHaveAnActiveProfile();\n            }\n        }\n\n        // Checks that pool was set\n        if (offeringAmountPool == 0 || raisingAmountPool == 0) {\n            revert PoolNotSet();\n        }\n\n        // Checks whether the timestamp is not too early\n        if (block.timestamp <= startTimestamp) {\n            revert TooEarly();\n        }\n\n        // Checks whether the timestamp is not too late\n        if (block.timestamp > endTimestamp) {\n            revert TooLate();\n        }\n\n        // Checks that the amount deposited is not inferior to 0\n        if (amount == 0) {\n            revert AmountMustExceedZero();\n        }\n    }\n\n    function HarvestPoolPreCheck(\n        uint256 endTimestamp,\n        uint256 amountPool,\n        bool claimedPool\n    ) internal view {\n        // Checks whether pool id is valid\n        if (block.timestamp <= endTimestamp) {\n            revert TooEarly();\n        }\n\n        // Checks whether the user has participated\n        if (amountPool == 0) {\n            revert DidNotParticipate();\n        }\n\n        // Checks whether the user has already harvested\n        if (claimedPool) {\n            revert AlreadyHarvested();\n        }\n    }\n\n    function FinalWithdrawPreCheck(\n        uint256 lpAmount,\n        uint256 lpTokenBalanceOf,\n        uint256 offerAmount,\n        uint256 offeringTokenBalanceOf\n    ) internal view {\n        if (lpAmount > lpTokenBalanceOf) {\n            revert NotEnoughLPTokens();\n        }\n\n        if (offerAmount > offeringTokenBalanceOf) {\n            revert NotEnoughOfferingTokens();\n        }\n    }\n\n    function SetPoolPreCheck(\n        uint256 startTimestamp,\n        bool hasTax,\n        uint256 flatTaxRate,\n        uint256 vestingPercentage,\n        uint256 vestingDuration,\n        uint256 vestingSlicePeriodSeconds\n    ) internal view {\n        if (block.timestamp >= startTimestamp) {\n            revert IFOHasStarted();\n        }\n\n        if (flatTaxRate >= 1e12) {\n            revert FlatTaxRateMustBeLessThan1e12();\n        }\n\n        if (vestingPercentage > 100) {\n            revert VestingPercentageShouldRangeIn0And100();\n        }\n\n        if (vestingDuration == 0) {\n            revert VestingDurationMustExceeds0();\n        }\n\n        if (vestingSlicePeriodSeconds < 1) {\n            revert VestingSlicePerSecondsMustBeExceeds1();\n        }\n\n        if (vestingSlicePeriodSeconds > vestingDuration) {\n            revert VestingSlicePerSecondsMustBeInteriorDuration();\n        }\n\n        if (!hasTax) {\n            if (flatTaxRate != 0) {\n                revert FlatTaxRateMustBe0WhenHasTaxIsFalse();\n            }\n        }\n    }\n\n    function UpdatePointParametersPreCheck(\n        uint256 endTimestamp\n    ) internal view {\n        if (block.timestamp >= endTimestamp) {\n            revert IFOHasEnded();\n        }\n    }\n\n    function UpdateStartAndEndTimestampsPreCheck(\n        uint256 MAX_BUFFER_SECONDS,\n        uint256 startAndEndTimestamps_length,\n        uint256 currentStartTimestamp,\n        uint256 startTimestamp,\n        uint256 endTimestamp\n    ) internal view {\n        if (startAndEndTimestamps_length != 2) {\n            revert StartAndEndTimestampsLengthNotCorrect();\n        }\n        if (endTimestamp >= (block.timestamp + MAX_BUFFER_SECONDS)) revert EndTimeTooFar();\n        if (block.timestamp >= currentStartTimestamp) revert IFOHasStarted();\n        if (startTimestamp >= endTimestamp) revert StartTimeMustInferiorToEndTime();\n        if (block.timestamp >= startTimestamp) revert StartTimeMustGreaterThanCurrentBlockTime();\n\n    }\n\n    function IsVestingInitializedPreCheck(bool isVestingInitialized) internal view {\n        if (!isVestingInitialized) {\n            revert VestingNotExist();\n        }\n    }\n}"
      },
      "contracts/utils/WhiteListV2.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport \"@openzeppelin-4.5.0/contracts/access/Ownable.sol\";\n\ncontract Whitelist is Ownable {\n    mapping(address => bool) private whitelist;\n\n    event WhitelistedAddressAdded(address indexed _user);\n    event WhitelistedAddressRemoved(address indexed _user);\n\n    /**\n     * @dev throws if user is not whitelisted.\n     * @param _user address\n     */\n    modifier onlyIfWhitelisted(address _user) {\n        require(whitelist[_user]);\n        _;\n    }\n\n    /**\n     * @dev add single address to whitelist\n     */\n    function addAddressToWhitelist(address _user) external onlyOwner {\n        whitelist[_user] = true;\n        emit WhitelistedAddressAdded(_user);\n    }\n\n    /**\n     * @dev add addresses to whitelist\n     */\n    function addAddressesToWhitelist(address[] calldata _users) external onlyOwner {\n        for (uint256 i = 0; i < _users.length; i++) {\n            whitelist[_users[i]] = true;\n            emit WhitelistedAddressAdded(_users[i]);\n        }\n    }\n\n    /**\n     * @dev remove single address from whitelist\n     */\n    function removeAddressFromWhitelist(address _user) external onlyOwner {\n        whitelist[_user] = false;\n        emit WhitelistedAddressRemoved(_user);\n    }\n\n    /**\n     * @dev remove addresses from whitelist\n     */\n    function removeAddressesFromWhitelist(address[] calldata _users) external onlyOwner {\n        for (uint256 i = 0; i < _users.length; i++) {\n            whitelist[_users[i]] = false;\n            emit WhitelistedAddressRemoved(_users[i]);\n        }\n    }\n\n    /**\n     * @dev getter to determine if address is in whitelist\n     */\n    function isWhitelisted(address _user) public view returns (bool) {\n        return whitelist[_user];\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 1
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}