false
false

Contract Address Details

0x513Dfa99283a36c246D9435cBE8fF948e17480a5

Contract Name
IFOInitializable
Creator
0x09df35–8f0521 at 0x44cbde–e98c11
Balance
0 KCS
Tokens
Fetching tokens...
Transactions
41 Transactions
Transfers
38 Transfers
Gas Used
3,012,179
Last Balance Update
50918456
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB 0x4fe6ed0a3e0264b6f0ca6c71ee06573879f129c1.
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
Verify & Publish
Contract name:
IFOInitializable




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
200
Verified at
2022-04-25T23:21:42.689431Z

Contract source code

// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

// File: @openzeppelin/contracts/utils/Context.sol
/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: @openzeppelin/contracts/math/SafeMath.sol
/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// File: @openzeppelin/contracts/utils/Address.sol
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: contracts/interfaces/IKRC20.sol
interface IKRC20 is IERC20 {
    /**
     * @dev Returns the token name.
     */
    function name() external view returns (string memory);

    /**
    * @dev Returns the token symbol.
    */
    function symbol() external view returns (string memory);

    /**
    * @dev Returns the token decimals.
    */
    function decimals() external view returns (uint8);

}

// File: @openzeppelin/contracts/utils/ReentrancyGuard.sol
/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

// File: contracts/interfaces/IIFOV2.sol
interface IIFOV2 {
    /**
     * @notice It allows users to deposit LP tokens to pool
     * @param _amount: the number of LP token used (18 decimals)
     * @param _pid: poolId
     */
    function depositPool(uint256 _amount, uint8 _pid) external;

    /**
     * @notice It allows users to harvest from pool
     * @param _pid: poolId
     */
    function harvestPool(uint8 _pid) external;

    /**
     * @notice It allows the admin to withdraw funds
     * @param _lpAmount: the number of LP token to withdraw (18 decimals)
     * @param _offerAmount: the number of offering amount to withdraw
     * @dev This function is only callable by admin.
     */
    function finalWithdraw(uint256 _lpAmount, uint256 _offerAmount) external;

    /**
     * @notice It sets parameters for pool
     * @param _offeringAmountPool: offering amount (in tokens)
     * @param _raisingAmountPool: raising amount (in LP tokens)
     * @param _limitPerUserInLP: limit per user (in LP tokens)
     * @param _hasTax: if the pool has a tax
     * @param _pid: poolId
     * @dev This function is only callable by admin.
     */
    function setPool(
      uint256 _offeringAmountPool,
      uint256 _raisingAmountPool,
      uint256 _limitPerUserInLP,
      bool _hasTax,
      uint8 _pid,
      address _whitelister,
      address _vester
    ) external;

    /**
     * @notice It updates point parameters for the IFO.
     * @param _numberPoints: the number of points for the IFO
     * @param _campaignId: the campaignId for the IFO
     * @param _thresholdPoints: the amount of LP required to receive points
     * @dev This function is only callable by admin.
     */
    function updatePointParameters(
        uint256 _campaignId,
        uint256 _numberPoints,
        uint256 _thresholdPoints
    ) external;

    // Struct that contains each pool characteristics
    struct PoolCharacteristics {
        uint256 raisingAmountPool; // amount of tokens raised for the pool (in LP tokens)
        uint256 offeringAmountPool; // amount of tokens offered for the pool (in offeringTokens)
        uint256 limitPerUserInLP; // limit of tokens per user (if 0, it is ignored)
        bool hasTax; // tax on the overflow (if any, it works with _calculateTaxOverflow)
        uint256 totalAmountPool; // total amount pool deposited (in LP tokens)
        uint256 sumTaxesOverflow; // total taxes collected (starts at 0, increases with each harvest if overflow)
        address whitelister;
        address vester;
    }

    /**
     * @notice It returns the pool information
     * @param _pid: poolId
     */
    function viewPoolInformation(uint256 _pid)
    external
    view
    returns (
        PoolCharacteristics memory
    );

    /**
     * @notice It returns the tax overflow rate calculated for a pool
     * @dev 100,000 means 0.1(10%)/ 1 means 0.000001(0.0001%)/ 1,000,000 means 1(100%)
     * @param _pid: poolId
     * @return It returns the tax percentage
     */
    function viewPoolTaxRateOverflow(uint256 _pid) external view returns (uint256);

    /**
     * @notice External view function to see user information
     * @param _user: user address
     * @param _pids[]: array of pids
     */
    function viewUserInfo(address _user, uint8[] calldata _pids)
    external
    view
    returns (uint256[] memory, bool[] memory);

    /**
     * @notice External view function to see user allocations for both pools
     * @param _user: user address
     * @param _pids[]: array of pids
     */
    function viewUserAllocationPools(address _user, uint8[] calldata _pids) external view returns (uint256[] memory);

    /**
     * @notice External view function to see user offering and refunding amounts for both pools
     * @param _user: user address
     * @param _pids: array of pids
     */
    function viewUserOfferingAndRefundingAmountsForPools(address _user, uint8[] calldata _pids)
    external
    view
    returns (uint256[3][] memory);

}

// File: contracts/interfaces/IWhitelistable.sol
interface IWhitelistable {
    /**
     * @dev Checks if account is whitelisted
     * @param _account The address to check
     */
    function isWhitelisted(address _account) external view returns (bool);
}

// File: contracts/interfaces/IVester.sol
interface IVester {
  function claimable(address _user) external view returns (uint256, bool);
  function setUserInfoForAccount(address _user, uint256 _offeringTokenAmount) external;
  function claim() external;
}

// File: @openzeppelin/contracts/utils/EnumerableSet.sol
/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

// File: @openzeppelin/contracts/access/AccessControl.sol
/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context {
    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view returns (bool) {
        return _roles[role].members.contains(account);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
        return _roles[role].members.length();
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
        return _roles[role].members.at(index);
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");

        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// File: @openzeppelin/contracts/utils/Counters.sol
/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
 * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
 * directly accessed.
 */
library Counters {
    using SafeMath for uint256;

    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        // The {SafeMath} overflow check can be skipped here, see the comment at the top
        counter._value += 1;
    }

    function decrement(Counter storage counter) internal {
        counter._value = counter._value.sub(1);
    }
}

// File: @openzeppelin/contracts/introspection/IERC165.sol
/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol
/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
      * @dev Safely transfers `tokenId` token from `from` to `to`.
      *
      * Requirements:
      *
      * - `from` cannot be the zero address.
      * - `to` cannot be the zero address.
      * - `tokenId` token must exist and be owned by `from`.
      * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
      * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
      *
      * Emits a {Transfer} event.
      */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

// File: @openzeppelin/contracts/token/ERC721/ERC721Holder.sol
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers. 
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
*/
contract ERC721Holder is IERC721Receiver {

    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

// File: contracts/MojitoProfile.sol
/** @title MojitoProfile.
@dev It is a contract for users to bind their address 
to a customizable profile by depositing a NFT.
*/
contract MojitoProfile is AccessControl, ERC721Holder, ReentrancyGuard {
    using Counters for Counters.Counter;
    using SafeERC20 for IKRC20;
    using SafeMath for uint256;

    IKRC20 public mojitoToken;

    bytes32 public constant NFT_ROLE = keccak256("NFT_ROLE");
    bytes32 public constant POINT_ROLE = keccak256("POINT_ROLE");
    bytes32 public constant SPECIAL_ROLE = keccak256("SPECIAL_ROLE");

    uint256 public numberActiveProfiles;
    uint256 public numberMojitoToReactivate;
    uint256 public numberMojitoToRegister;
    uint256 public numberMojitoToUpdate;
    uint256 public numberTeams;

    mapping(address => bool) public hasRegistered;

    mapping(uint256 => Team) private teams;
    mapping(address => User) private users;

    // Used for generating the teamId
    Counters.Counter private _countTeams;

    // Used for generating the userId
    Counters.Counter private _countUsers;

    // Event to notify a new team is created
    event TeamAdd(uint256 teamId, string teamName);

    // Event to notify that team points are increased
    event TeamPointIncrease(
        uint256 indexed teamId,
        uint256 numberPoints,
        uint256 indexed campaignId
    );

    event UserChangeTeam(
        address indexed userAddress,
        uint256 oldTeamId,
        uint256 newTeamId
    );

    // Event to notify that a user is registered
    event UserNew(
        address indexed userAddress,
        uint256 teamId,
        address nftAddress,
        uint256 tokenId
    );

    // Event to notify a user pausing profile
    event UserPause(address indexed userAddress, uint256 teamId);

    // Event to notify that user points are increased
    event UserPointIncrease(
        address indexed userAddress,
        uint256 numberPoints,
        uint256 indexed campaignId
    );

    // Event to notify that a list of users have an increase in points
    event UserPointIncreaseMultiple(
        address[] userAddresses,
        uint256 numberPoints,
        uint256 indexed campaignId
    );

    // Event to notify that a user is reactivating profile
    event UserReactivate(
        address indexed userAddress,
        uint256 teamId,
        address nftAddress,
        uint256 tokenId
    );

    // Event to notify that a user is pausing profile
    event UserUpdate(
        address indexed userAddress,
        address nftAddress,
        uint256 tokenId
    );

    // Modifier for admin roles
    modifier onlyOwner() {
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "MojitoProfile::onlyOwner: Not the main admin");
        _;
    }

    // Modifier for point roles
    modifier onlyPoint() {
        require(hasRole(POINT_ROLE, _msgSender()), "MojitoProfile::onlyPoint: Not a point admin");
        _;
    }

    // Modifier for special roles
    modifier onlySpecial() {
        require(hasRole(SPECIAL_ROLE, _msgSender()), "MojitoProfile::onlySpecial: Not a special admin");
        _;
    }

    struct Team {
        string teamName;
        string teamDescription;
        uint256 numberUsers;
        uint256 numberPoints;
        bool isJoinable;
    }

    struct User {
        uint256 userId;
        uint256 numberPoints;
        uint256 teamId;
        address nftAddress;
        uint256 tokenId;
        bool isActive;
    }

    constructor(
        IKRC20 _mojitoToken,
        uint256 _numberMojitoToReactivate,
        uint256 _numberMojitoToRegister,
        uint256 _numberMojitoToUpdate
    ) public {
        mojitoToken = _mojitoToken;
        numberMojitoToReactivate = _numberMojitoToReactivate;
        numberMojitoToRegister = _numberMojitoToRegister;
        numberMojitoToUpdate = _numberMojitoToUpdate;
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    /**
     * @dev To create a user profile. It sends the NFT to the contract
     * and sends MJT to burn address. Requires 2 token approvals.
     */
    function createProfile(uint256 _teamId, address _nftAddress, uint256 _tokenId) external nonReentrant {
        require(!hasRegistered[_msgSender()], "MojitoProfile::createProfile: Already registered");
        require((_teamId <= numberTeams) && (_teamId > 0), "MojitoProfile::createProfile: Invalid teamId");
        require(teams[_teamId].isJoinable, "MojitoProfile::createProfile: Team not joinable");
        require(hasRole(NFT_ROLE, _nftAddress), "MojitoProfile::createProfile: NFT address invalid");

        // Loads the interface to deposit the NFT contract
        IERC721 nftToken = IERC721(_nftAddress);

        require(_msgSender() == nftToken.ownerOf(_tokenId), "MojitoProfile::createProfile: Only NFT owner can register");

        // Transfer NFT to this contract
        nftToken.safeTransferFrom(_msgSender(), address(this), _tokenId);

        // Transfer MJT tokens to this contract
        mojitoToken.safeTransferFrom(_msgSender(), address(this), numberMojitoToRegister);

        // Increment the _countUsers counter and get userId
        _countUsers.increment();
        uint256 newUserId = _countUsers.current();

        // Add data to the struct for newUserId
        users[_msgSender()] = User({
        userId : newUserId,
        numberPoints : 0,
        teamId : _teamId,
        nftAddress : _nftAddress,
        tokenId : _tokenId,
        isActive : true
        });

        // Update registration status
        hasRegistered[_msgSender()] = true;

        // Update number of active profiles
        numberActiveProfiles = numberActiveProfiles.add(1);

        // Increase the number of users for the team
        teams[_teamId].numberUsers = teams[_teamId].numberUsers.add(1);

        // Emit an event
        emit UserNew(_msgSender(), _teamId, _nftAddress, _tokenId);
    }

    /**
     * @dev To pause user profile. It releases the NFT.
     * Callable only by registered users.
     */
    function pauseProfile() external nonReentrant {
        require(hasRegistered[_msgSender()], "MojitoProfile::pauseProfile: Has not registered");

        // Checks whether user has already paused
        require(users[_msgSender()].isActive, "MojitoProfile::pauseProfile: User not active");

        // Change status of user to make it inactive
        users[_msgSender()].isActive = false;

        // Retrieve the teamId of the user calling
        uint256 userTeamId = users[_msgSender()].teamId;

        // Reduce number of active users and team users
        teams[userTeamId].numberUsers = teams[userTeamId].numberUsers.sub(1);
        numberActiveProfiles = numberActiveProfiles.sub(1);

        // Interface to deposit the NFT contract
        IERC721 nftToken = IERC721(users[_msgSender()].nftAddress);

        // tokenId of NFT redeemed
        uint256 redeemedTokenId = users[_msgSender()].tokenId;

        // Change internal statuses as extra safety
        users[_msgSender()].nftAddress = address(0x0);

        users[_msgSender()].tokenId = 0;

        // Transfer the NFT back to the user
        nftToken.safeTransferFrom(address(this), _msgSender(), redeemedTokenId);

        // Emit event
        emit UserPause(_msgSender(), userTeamId);
    }

    /**
     * @dev To update user profile.
     * Callable only by registered users.
     */
    function updateProfile(address _nftAddress, uint256 _tokenId) external nonReentrant {
        require(hasRegistered[_msgSender()], "MojitoProfile::updateProfile: Has not registered");
        require(hasRole(NFT_ROLE, _nftAddress), "MojitoProfile::updateProfile: NFT address invalid");
        require(users[_msgSender()].isActive, "MojitoProfile::updateProfile: User not active");

        address currentAddress = users[_msgSender()].nftAddress;
        uint256 currentTokenId = users[_msgSender()].tokenId;

        // Interface to deposit the NFT contract
        IERC721 nftNewToken = IERC721(_nftAddress);

        require(_msgSender() == nftNewToken.ownerOf(_tokenId), "MojitoProfile::updateProfile: Only NFT owner can update");

        // Transfer token to new address
        nftNewToken.safeTransferFrom(_msgSender(), address(this), _tokenId);

        // Transfer MJT token to this address
        mojitoToken.safeTransferFrom(_msgSender(), address(this), numberMojitoToUpdate);

        // Interface to deposit the NFT contract
        IERC721 nftCurrentToken = IERC721(currentAddress);

        // Transfer old token back to the owner
        nftCurrentToken.safeTransferFrom(address(this), _msgSender(), currentTokenId);

        // Update mapping in storage
        users[_msgSender()].nftAddress = _nftAddress;
        users[_msgSender()].tokenId = _tokenId;

        emit UserUpdate(_msgSender(), _nftAddress, _tokenId);
    }

    /**
     * @dev To reactivate user profile.
     * Callable only by registered users.
     */
    function reactivateProfile(address _nftAddress, uint256 _tokenId) external nonReentrant {
        require(hasRegistered[_msgSender()], "MojitoProfile::reactivateProfile: Has not registered");
        require(hasRole(NFT_ROLE, _nftAddress), "MojitoProfile::reactivateProfile: NFT address invalid");
        require(!users[_msgSender()].isActive, "MojitoProfile::reactivateProfile: User is active");

        // Interface to deposit the NFT contract
        IERC721 nftToken = IERC721(_nftAddress);
        require(_msgSender() == nftToken.ownerOf(_tokenId), "MojitoProfile::reactivateProfile: Only NFT owner can update");

        // Transfer NFT to contract
        nftToken.safeTransferFrom(_msgSender(), address(this), _tokenId);

        // Transfer to this address
        mojitoToken.safeTransferFrom(_msgSender(), address(this), numberMojitoToReactivate);

        // Retrieve teamId of the user
        uint256 userTeamId = users[_msgSender()].teamId;

        // Update number of users for the team and number of active profiles
        numberActiveProfiles = numberActiveProfiles.add(1);
        teams[userTeamId].numberUsers = teams[userTeamId].numberUsers.add(1);

        // Update user statuses
        users[_msgSender()].isActive = true;
        users[_msgSender()].nftAddress = _nftAddress;
        users[_msgSender()].tokenId = _tokenId;

        // Emit event
        emit UserReactivate(_msgSender(), userTeamId, _nftAddress, _tokenId);
    }

    /**
     * @dev To increase the number of points for a user.
     * Callable only by point admins
     */
    function increaseUserPoints(address _userAddress, uint256 _numberPoints, uint256 _campaignId) external onlyPoint {
        // Increase the number of points for the user
        users[_userAddress].numberPoints = users[_userAddress].numberPoints.add(_numberPoints);

        emit UserPointIncrease(_userAddress, _numberPoints, _campaignId);
    }

    /**
     * @dev To increase the number of points for a set of users.
     * Callable only by point admins
     */
    function increaseUserPointsMultiple(address[] calldata _userAddresses, uint256 _numberPoints, uint256 _campaignId) external onlyPoint {
        require(_userAddresses.length < 1001, "MojitoProfile::increaseUserPointsMultiple: Length must be < 1001");

        for (uint256 i = 0; i < _userAddresses.length; i++) {
            users[_userAddresses[i]].numberPoints = users[_userAddresses[i]].numberPoints.add(_numberPoints);
        }

        emit UserPointIncreaseMultiple(_userAddresses, _numberPoints, _campaignId);
    }

    /**
     * @dev To increase the number of points for a team.
     * Callable only by point admins
     */
    function increaseTeamPoints(uint256 _teamId, uint256 _numberPoints, uint256 _campaignId) external onlyPoint {
        // Increase the number of points for the team
        teams[_teamId].numberPoints = teams[_teamId].numberPoints.add(_numberPoints);

        emit TeamPointIncrease(_teamId, _numberPoints, _campaignId);
    }

    /**
     * @dev To remove the number of points for a user.
     * Callable only by point admins
     */
    function removeUserPoints(address _userAddress, uint256 _numberPoints) external onlyPoint {
        // Increase the number of points for the user
        users[_userAddress].numberPoints = users[_userAddress].numberPoints.sub(_numberPoints);
    }

    /**
     * @dev To remove a set number of points for a set of users.
     */
    function removeUserPointsMultiple(address[] calldata _userAddresses, uint256 _numberPoints) external onlyPoint {
        require(_userAddresses.length < 1001, "MojitoProfile::removeUserPointsMultiple: Length must be < 1001");
        for (uint256 i = 0; i < _userAddresses.length; i++) {
            users[_userAddresses[i]].numberPoints = users[_userAddresses[i]].numberPoints.sub(_numberPoints);
        }
    }

    /**
     * @dev To remove the number of points for a team.
     * Callable only by point admins
     */
    function removeTeamPoints(uint256 _teamId, uint256 _numberPoints) external onlyPoint {
        // Increase the number of points for the team
        teams[_teamId].numberPoints = teams[_teamId].numberPoints.sub(_numberPoints);
    }

    /**
     * @dev To add a NFT contract address for users to set their profile.
     * Callable only by owner admins.
     */
    function addNftAddress(address _nftAddress) external onlyOwner {
        require(IERC721(_nftAddress).supportsInterface(0x80ac58cd), "MojitoProfile::addNftAddress: Not ERC721");
        grantRole(NFT_ROLE, _nftAddress);
    }

    /**
     * @dev Add a new teamId
     * Callable only by owner admins.
     */
    function addTeam(string calldata _teamName, string calldata _teamDescription) external onlyOwner {
        // Verify length is between 3 and 20
        bytes memory strBytes = bytes(_teamName);
        require(strBytes.length < 20, "MojitoProfile::addTeam: Must be < 20");
        require(strBytes.length > 3, "MojitoProfile::addTeam: Must be > 3");

        // Increment the _countTeams counter and get teamId
        _countTeams.increment();
        uint256 newTeamId = _countTeams.current();

        // Add new team data to the struct
        teams[newTeamId] = Team({
        teamName : _teamName,
        teamDescription : _teamDescription,
        numberUsers : 0,
        numberPoints : 0,
        isJoinable : true
        });

        numberTeams = newTeamId;
        emit TeamAdd(newTeamId, _teamName);
    }

    /**
     * @dev Function to change team.
     * Callable only by special admins.
     */
    function changeTeam(address _userAddress, uint256 _newTeamId) external onlySpecial {
        require(hasRegistered[_userAddress], "MojitoProfile::changeTeam: Has not registered");
        require((_newTeamId <= numberTeams) && (_newTeamId > 0), "MojitoProfile::changeTeam: Invalid teamId");
        require(teams[_newTeamId].isJoinable, "MojitoProfile::changeTeam: Team not joinable");
        require(users[_userAddress].teamId != _newTeamId, "MojitoProfile::changeTeam: Already in the team");

        // Get old teamId
        uint256 oldTeamId = users[_userAddress].teamId;

        // Change number of users in old team
        teams[oldTeamId].numberUsers = teams[oldTeamId].numberUsers.sub(1);

        // Change teamId in user mapping
        users[_userAddress].teamId = _newTeamId;

        // Change number of users in new team
        teams[_newTeamId].numberUsers = teams[_newTeamId].numberUsers.add(1);

        emit UserChangeTeam(_userAddress, oldTeamId, _newTeamId);
    }

    /**
     * @dev Claim MJT to burn later.
     * Callable only by owner admins.
     */
    function claimFee(uint256 _amount) external onlyOwner {
        mojitoToken.safeTransfer(_msgSender(), _amount);
    }

    /**
     * @dev Make a team joinable again.
     * Callable only by owner admins.
     */
    function makeTeamJoinable(uint256 _teamId) external onlyOwner {
        require((_teamId <= numberTeams) && (_teamId > 0), "MojitoProfile::makeTeamJoinable: Invalid teamId");
        teams[_teamId].isJoinable = true;
    }

    /**
     * @dev Make a team not joinable.
     * Callable only by owner admins.
     */
    function makeTeamNotJoinable(uint256 _teamId) external onlyOwner {
        require((_teamId <= numberTeams) && (_teamId > 0), "MojitoProfile::makeTeamNotJoinable: Invalid teamId");
        teams[_teamId].isJoinable = false;
    }

    /**
     * @dev Rename a team
     * Callable only by owner admins.
     */
    function renameTeam(uint256 _teamId, string calldata _teamName, string calldata _teamDescription) external onlyOwner {
        require((_teamId <= numberTeams) && (_teamId > 0), "MojitoProfile::renameTeam: Invalid teamId");

        // Verify length is between 3 and 20
        bytes memory strBytes = bytes(_teamName);
        require(strBytes.length < 20, "MojitoProfile::renameTeam: Must be < 20");
        require(strBytes.length > 3, "MojitoProfile::renameTeam: Must be > 3");

        teams[_teamId].teamName = _teamName;
        teams[_teamId].teamDescription = _teamDescription;
    }

    /**
     * @dev Update the number of MJT to register
     * Callable only by owner admins.
     */
    function updateNumberMojito(uint256 _newNumberMojitoToReactivate, uint256 _newNumberMojitoToRegister, uint256 _newNumberMojitoToUpdate) external onlyOwner {
        numberMojitoToReactivate = _newNumberMojitoToReactivate;
        numberMojitoToRegister = _newNumberMojitoToRegister;
        numberMojitoToUpdate = _newNumberMojitoToUpdate;
    }

    /**
     * @dev Check the user's profile for a given address
     */
    function getUserProfile(address _userAddress)
    external
    view
    returns (
        uint256,
        uint256,
        uint256,
        address,
        uint256,
        bool
    )
    {
        require(hasRegistered[_userAddress], "MojitoProfile::getUserProfile: Has not registered");
        return (
        users[_userAddress].userId,
        users[_userAddress].numberPoints,
        users[_userAddress].teamId,
        users[_userAddress].nftAddress,
        users[_userAddress].tokenId,
        users[_userAddress].isActive
        );
    }

    /**
     * @dev Check the user's status for a given address
     */
    function getUserStatus(address _userAddress) external view returns (bool) {
        return (users[_userAddress].isActive);
    }

    /**
     * @dev Check a team's profile
     */
    function getTeamProfile(uint256 _teamId)
    external
    view
    returns (
        string memory,
        string memory,
        uint256,
        uint256,
        bool
    )
    {
        require((_teamId <= numberTeams) && (_teamId > 0), "MojitoProfile::getTeamProfile: Invalid teamId");
        return (
        teams[_teamId].teamName,
        teams[_teamId].teamDescription,
        teams[_teamId].numberUsers,
        teams[_teamId].numberPoints,
        teams[_teamId].isJoinable
        );
    }
}

// File: contracts/IFOInitializable.sol
contract IFOInitializable is IIFOV2, ReentrancyGuard, Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IKRC20;

    // Number of pools
    uint8 public constant NUMBER_POOLS = 2;

    // The address of the smart chef factory
    address public immutable IFO_FACTORY;

    // Max blocks (for sanity checks)
    uint256 public MAX_BUFFER_BLOCKS;

    // The LP token used
    IKRC20 public lpToken;

    // The offering token
    IKRC20 public offeringToken;

    // MojitoProfile
    MojitoProfile public mojitoProfile;

    // Whether it is initialized
    bool public isInitialized;

    // The block number when IFO starts
    uint256 public startBlock;

    // The block number when IFO ends
    uint256 public endBlock;

    // The block number when IFO starts harvest
    uint256 public harvestBlock;

    // The campaignId for the IFO
    uint256 public campaignId;

    // The number of points distributed to each person who harvest
    uint256 public numberPoints;

    // The threshold for points (in LP tokens)
    uint256 public thresholdPoints;

    // Total tokens distributed across the pools
    uint256 public totalTokensOffered;

    // Array of PoolCharacteristics of size NUMBER_POOLS
    PoolCharacteristics[NUMBER_POOLS] private _poolInformation;

    // Checks if user has claimed points
    mapping(address => bool) private _hasClaimedPoints;

    // It maps the address to pool id to UserInfo
    mapping(address => mapping(uint8 => UserInfo)) private _userInfo;

    // Struct that contains each user information for both pools
    struct UserInfo {
        uint256 amountPool; // How many tokens the user has provided for pool
        bool claimedPool; // Whether the user has claimed (default: false) for pool
    }

    // Admin withdraw events
    event AdminWithdraw(uint256 amountLP, uint256 amountOfferingToken);

    // Admin recovers token
    event AdminTokenRecovery(address tokenAddress, uint256 amountTokens);

    // Deposit event
    event Deposit(address indexed user, uint256 amount, uint8 indexed pid);

    // Harvest event
    event Harvest(address indexed user, uint256 offeringAmount, uint256 excessAmount, uint8 indexed pid);

    // Event for new start & end blocks
    event NewStartAndEndBlocks(uint256 startBlock, uint256 endBlock);

    // Event for harvest blocks
    event NewHarvestBlocks(uint256 harvestBlock);

    // Event with point parameters for IFO
    event PointParametersSet(uint256 campaignId, uint256 numberPoints, uint256 thresholdPoints);

    // Event when parameters are set for one of the pools
    event PoolParametersSet(uint256 offeringAmountPool, uint256 raisingAmountPool, uint8 pid);

    // Modifier to prevent contracts to participate
    modifier notContract() {
        require(!_isContract(msg.sender), "IFOInitializable::notContract: Contract not allowed");
        require(msg.sender == tx.origin, "IFOInitializable::notContract: Proxy contract not allowed");
        _;
    }

    /**
     * @notice Constructor
     */
    constructor() public {
        IFO_FACTORY = msg.sender;
    }

    /**
     * @notice It initializes the contract
     * @dev It can only be called once.
     * @param _lpToken: the LP token used
     * @param _offeringToken: the token that is offered for the IFO
     * @param _mojitoProfileAddress: the address of the MojitoProfile
     * @param _startBlock: the start block for the IFO
     * @param _endBlock: the end block for the IFO
     * @param _maxBufferBlocks: maximum buffer of blocks from the current block number
     * @param _adminAddress: the admin address for handling tokens
     */
    function initialize(
        address _lpToken,
        address _offeringToken,
        address _mojitoProfileAddress,
        uint256 _startBlock,
        uint256 _endBlock,
        uint256 _maxBufferBlocks,
        address _adminAddress
    ) public {
        require(!isInitialized, "IFOInitializable::initialize: Already initialized");
        require(msg.sender == IFO_FACTORY, "IFOInitializable::initialize: Not factory");

        // Make this contract initialized
        isInitialized = true;

        lpToken = IKRC20(_lpToken);
        offeringToken = IKRC20(_offeringToken);
        mojitoProfile = MojitoProfile(_mojitoProfileAddress);
        startBlock = _startBlock;
        endBlock = _endBlock;
        harvestBlock = _endBlock;
        MAX_BUFFER_BLOCKS = _maxBufferBlocks;

        // Transfer ownership to admin
        transferOwnership(_adminAddress);
    }

    /**
     * @notice It allows users to deposit LP tokens to pool
     * @param _amount: the number of LP token used (18 decimals)
     * @param _pid: pool id
     */
    function depositPool(uint256 _amount, uint8 _pid) external override nonReentrant notContract {
        // Checks whether the user has an active profile
        require(mojitoProfile.getUserStatus(msg.sender), "IFOInitializable::depositPool: Must have an active profile");

        // Checks whether the pool id is valid
        require(_pid < NUMBER_POOLS, "IFOInitializable::depositPool: Non valid pool id");

        // Checks if account is whitelisted, must check if pid is valid first
        require(_checkWhitelistStatus(_pid, msg.sender), "IFOInitializable::depositPool: This address is not in the whitelist");

        // Checks that pool was set
        require(
            _poolInformation[_pid].offeringAmountPool > 0 && _poolInformation[_pid].raisingAmountPool > 0,
            "IFOInitializable::depositPool: Pool not set"
        );

        // Checks whether the block number is not too early
        require(block.number > startBlock, "IFOInitializable::depositPool: Too early");

        // Checks whether the block number is not too late
        require(block.number < endBlock, "IFOInitializable::depositPool: Too late");

        // Checks that the amount deposited is not inferior to 0
        require(_amount > 0, "IFOInitializable::depositPool: Amount must be > 0");

        address vester = _poolInformation[_pid].vester;
        if (vester == address(0)) {
            // Verify tokens were deposited properly
            require(offeringToken.balanceOf(address(this)) >= totalTokensOffered, "IFOInitializable::depositPool: Tokens not deposited properly");
        }

        // Transfers funds to this contract
        lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);

        // Update the user status
        _userInfo[msg.sender][_pid].amountPool = _userInfo[msg.sender][_pid].amountPool.add(_amount);

        // Check if the pool has a limit per user
        if (_poolInformation[_pid].limitPerUserInLP > 0) {
            // Checks whether the limit has been reached
            require(
                _userInfo[msg.sender][_pid].amountPool <= _poolInformation[_pid].limitPerUserInLP,
                "IFOInitializable::depositPool: New amount above user limit"
            );
        }

        // Updates the totalAmount for pool
        _poolInformation[_pid].totalAmountPool = _poolInformation[_pid].totalAmountPool.add(_amount);

        emit Deposit(msg.sender, _amount, _pid);
    }

    /**
     * @notice It allows users to harvest from pool
     * @param _pid: pool id
     */
    function harvestPool(uint8 _pid) external override nonReentrant notContract {
        // Checks whether it is too early to harvest
        require(block.number > harvestBlock, "IFOInitializable::harvestPool: Too early");

        // Checks whether pool id is valid
        require(_pid < NUMBER_POOLS, "IFOInitializable::harvestPool: Non valid pool id");

        // Checks whether the user has participated
        require(_userInfo[msg.sender][_pid].amountPool > 0, "IFOInitializable::harvestPool: Did not participate");

        // Checks whether the user has already harvested
        require(!_userInfo[msg.sender][_pid].claimedPool, "IFOInitializable::harvestPool: Already done");

        // Claim points if possible
        _claimPoints(msg.sender);

        // Updates the harvest status
        _userInfo[msg.sender][_pid].claimedPool = true;

        // Initialize the variables for offering, refunding user amounts, and tax amount
        (
        uint256 offeringTokenAmount,
        uint256 refundingTokenAmount,
        uint256 userTaxOverflow
        ) = _calculateOfferingAndRefundingAmountsPool(msg.sender, _pid);

        // Increment the sumTaxesOverflow
        if (userTaxOverflow > 0) {
            _poolInformation[_pid].sumTaxesOverflow = _poolInformation[_pid].sumTaxesOverflow.add(userTaxOverflow);
        }

        // Transfer these tokens back to the user if quantity > 0
        if (offeringTokenAmount > 0) {
            address vester = _poolInformation[_pid].vester;
            if (vester == address(0)) {
                offeringToken.safeTransfer(address(msg.sender), offeringTokenAmount);
            } else {
                IVester(vester).setUserInfoForAccount(msg.sender, offeringTokenAmount);
            }
        }

        if (refundingTokenAmount > 0) {
            lpToken.safeTransfer(address(msg.sender), refundingTokenAmount);
        }

        emit Harvest(msg.sender, offeringTokenAmount, refundingTokenAmount, _pid);
    }

    /**
     * @notice It allows the admin to withdraw funds
     * @param _lpAmount: the number of LP token to withdraw (18 decimals)
     * @param _offerAmount: the number of offering amount to withdraw
     * @dev This function is only callable by admin.
     */
    function finalWithdraw(uint256 _lpAmount, uint256 _offerAmount) external override onlyOwner {
        require(_lpAmount <= lpToken.balanceOf(address(this)), "IFOInitializable::finalWithdraw: Not enough LP tokens");
        require(_offerAmount <= offeringToken.balanceOf(address(this)), "IFOInitializable::finalWithdraw: Not enough offering tokens");

        if (_lpAmount > 0) {
            lpToken.safeTransfer(address(msg.sender), _lpAmount);
        }

        if (_offerAmount > 0) {
            offeringToken.safeTransfer(address(msg.sender), _offerAmount);
        }

        emit AdminWithdraw(_lpAmount, _offerAmount);
    }

    /**
     * @notice It allows the admin to recover wrong tokens sent to the contract
     * @param _tokenAddress: the address of the token to withdraw (18 decimals)
     * @param _tokenAmount: the number of token amount to withdraw
     * @dev This function is only callable by admin.
     */
    function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
        require(_tokenAddress != address(lpToken), "IFOInitializable::recoverWrongTokens: Cannot be LP token");
        require(_tokenAddress != address(offeringToken), "IFOInitializable::recoverWrongTokens: Cannot be offering token");

        IKRC20(_tokenAddress).safeTransfer(address(msg.sender), _tokenAmount);

        emit AdminTokenRecovery(_tokenAddress, _tokenAmount);
    }

    /**
     * @notice It sets parameters for pool
     * @param _offeringAmountPool: offering amount (in tokens)
     * @param _raisingAmountPool: raising amount (in LP tokens)
     * @param _limitPerUserInLP: limit per user (in LP tokens)
     * @param _hasTax: if the pool has a tax
     * @param _pid: pool id
     * @dev This function is only callable by admin.
     */
    function setPool(
        uint256 _offeringAmountPool,
        uint256 _raisingAmountPool,
        uint256 _limitPerUserInLP,
        bool _hasTax,
        uint8 _pid,
        address _whitelister,
        address _vester
    ) external override onlyOwner {
        require(block.number < startBlock, "IFOInitializable::setPool: IFO has started");
        require(_pid < NUMBER_POOLS, "IFOInitializable::setPool: Pool does not exist");

        _poolInformation[_pid].offeringAmountPool = _offeringAmountPool;
        _poolInformation[_pid].raisingAmountPool = _raisingAmountPool;
        _poolInformation[_pid].limitPerUserInLP = _limitPerUserInLP;
        _poolInformation[_pid].hasTax = _hasTax;
        _poolInformation[_pid].whitelister = _whitelister;
        _poolInformation[_pid].vester = _vester;

        uint256 tokensDistributedAcrossPools;

        for (uint8 i = 0; i < NUMBER_POOLS; i++) {
            tokensDistributedAcrossPools = tokensDistributedAcrossPools.add(_poolInformation[i].offeringAmountPool);
        }

        // Update totalTokensOffered
        totalTokensOffered = tokensDistributedAcrossPools;

        emit PoolParametersSet(_offeringAmountPool, _raisingAmountPool, _pid);
    }

    /**
     * @notice It updates point parameters for the IFO.
     * @param _campaignId: the campaignId for the IFO
     * @param _numberPoints: the number of points for the IFO
     * @param _thresholdPoints: the amount of LP required to receive points
     * @dev This function is only callable by admin.
     */
    function updatePointParameters(
        uint256 _campaignId,
        uint256 _numberPoints,
        uint256 _thresholdPoints
    ) external override onlyOwner {
        require(block.number < endBlock, "IFOInitializable::updatePointParameters: IFO has ended");

        numberPoints = _numberPoints;
        campaignId = _campaignId;
        thresholdPoints = _thresholdPoints;

        emit PointParametersSet(campaignId, numberPoints, thresholdPoints);
    }

    /**
     * @notice It allows the admin to update start and end blocks
     * @param _startBlock: the new start block
     * @param _endBlock: the new end block
     * @dev This function is only callable by admin.
     */
    function updateStartAndEndBlocks(uint256 _startBlock, uint256 _endBlock) external onlyOwner {
        require(_endBlock < (block.number + MAX_BUFFER_BLOCKS), "IFOInitializable::updateStartAndEndBlocks: EndBlock too far");
        require(block.number < startBlock, "IFOInitializable::updateStartAndEndBlocks: IFO has started");
        require(_startBlock < _endBlock, "IFOInitializable::updateStartAndEndBlocks: New startBlock must be lower than new endBlock");
        require(block.number < _startBlock, "IFOInitializable::updateStartAndEndBlocks: New startBlock must be higher than current block");

        startBlock = _startBlock;
        endBlock = _endBlock;

        emit NewStartAndEndBlocks(_startBlock, _endBlock);
    }

    /**
     * @notice It allows the admin to update harvest blocks
     * @param _harvestBlock: the new harvest block
     * @dev This function is only callable by admin.
     */
    function updateHarvestBlocks(uint256 _harvestBlock) external onlyOwner {
        require(block.number < startBlock, "IFOInitializable::updateHarvestBlocks: IFO has started");
        require(endBlock < _harvestBlock, "IFOInitializable::updateHarvestBlocks: New harvestBlock must be higher than endBlock");

        harvestBlock = _harvestBlock;

        emit NewHarvestBlocks(_harvestBlock);
    }

    /**
     * @notice It returns the pool information
     * @param _pid: poolId
     */
    function viewPoolInformation(uint256 _pid)
    external
    view
    override
    returns (PoolCharacteristics memory)
    {
        return (
        _poolInformation[_pid]
        );
    }

    /**
     * @notice It returns the tax overflow rate calculated for a pool
     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)
     * @param _pid: poolId
     * @return It returns the tax percentage
     */
    function viewPoolTaxRateOverflow(uint256 _pid) external view override returns (uint256) {
        if (!_poolInformation[_pid].hasTax) {
            return 0;
        } else {
            return
            _calculateTaxOverflow(_poolInformation[_pid].totalAmountPool, _poolInformation[_pid].raisingAmountPool);
        }
    }

    /**
     * @notice External view function to see user allocations for both pools
     * @param _user: user address
     * @param _pids[]: array of pids
     * @return
     */
    function viewUserAllocationPools(address _user, uint8[] calldata _pids)
    external
    view
    override
    returns (uint256[] memory)
    {
        uint256[] memory allocationPools = new uint256[](_pids.length);
        for (uint8 i = 0; i < _pids.length; i++) {
            allocationPools[i] = _getUserAllocationPool(_user, _pids[i]);
        }
        return allocationPools;
    }

    /**
     * @notice External view function to see user information
     * @param _user: user address
     * @param _pids[]: array of pids
     */
    function viewUserInfo(address _user, uint8[] calldata _pids)
    external
    view
    override
    returns (uint256[] memory, bool[] memory)
    {
        uint256[] memory amountPools = new uint256[](_pids.length);
        bool[] memory statusPools = new bool[](_pids.length);

        for (uint8 i = 0; i < NUMBER_POOLS; i++) {
            amountPools[i] = _userInfo[_user][i].amountPool;
            statusPools[i] = _userInfo[_user][i].claimedPool;
        }
        return (amountPools, statusPools);
    }

    /**
     * @notice External view function to see user offering and refunding amounts for both pools
     * @param _user: user address
     * @param _pids: array of pids
     */
    function viewUserOfferingAndRefundingAmountsForPools(address _user, uint8[] calldata _pids)
    external
    view
    override
    returns (uint256[3][] memory)
    {
        uint256[3][] memory amountPools = new uint256[3][](_pids.length);

        for (uint8 i = 0; i < _pids.length; i++) {
            uint256 userOfferingAmountPool;
            uint256 userRefundingAmountPool;
            uint256 userTaxAmountPool;

            if (_poolInformation[_pids[i]].raisingAmountPool > 0) {
                (
                userOfferingAmountPool,
                userRefundingAmountPool,
                userTaxAmountPool
                ) = _calculateOfferingAndRefundingAmountsPool(_user, _pids[i]);
            }

            amountPools[i] = [userOfferingAmountPool, userRefundingAmountPool, userTaxAmountPool];
        }
        return amountPools;
    }

    /**
     * @notice It allows users to claim points
     * @param _user: user address
     */
    function _claimPoints(address _user) internal {
        if (!_hasClaimedPoints[_user]) {
            uint256 sumPools;
            for (uint8 i = 0; i < NUMBER_POOLS; i++) {
                sumPools = sumPools.add(_userInfo[msg.sender][i].amountPool);
            }
            if (sumPools > thresholdPoints) {
                _hasClaimedPoints[_user] = true;
                // Increase user points
                mojitoProfile.increaseUserPoints(msg.sender, numberPoints, campaignId);
            }
        }
    }

    /**
     * @notice It calculates the tax overflow given the raisingAmountPool and the totalAmountPool.
     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)
     * @return It returns the tax percentage
     */
    function _calculateTaxOverflow(uint256 _totalAmountPool, uint256 _raisingAmountPool)
    internal
    pure
    returns (uint256)
    {
        uint256 ratioOverflow = _totalAmountPool.div(_raisingAmountPool);

        if (ratioOverflow >= 1500) {
            return 500000000;
            // 0.05%
        } else if (ratioOverflow >= 1000) {
            return 1000000000;
            // 0.1%
        } else if (ratioOverflow >= 500) {
            return 2000000000;
            // 0.2%
        } else if (ratioOverflow >= 250) {
            return 2500000000;
            // 0.25%
        } else if (ratioOverflow >= 100) {
            return 3000000000;
            // 0.3%
        } else if (ratioOverflow >= 50) {
            return 5000000000;
            // 0.5%
        } else {
            return 10000000000;
            // 1%
        }
    }

    /**
     * @notice It calculates the offering amount for a user and the number of LP tokens to transfer back.
     * @param _user: user address
     * @param _pid: pool id
     * @return {uint256, uint256, uint256} It returns the offering amount, the refunding amount (in LP tokens),
     * and the tax (if any, else 0)
     */
    function _calculateOfferingAndRefundingAmountsPool(address _user, uint8 _pid)
    internal
    view
    returns (
        uint256,
        uint256,
        uint256
    )
    {
        uint256 userOfferingAmount;
        uint256 userRefundingAmount;
        uint256 taxAmount;

        if (_poolInformation[_pid].totalAmountPool > _poolInformation[_pid].raisingAmountPool) {
            // Calculate allocation for the user
            uint256 allocation = _getUserAllocationPool(_user, _pid);

            // Calculate the offering amount for the user based on the offeringAmount for the pool
            userOfferingAmount = _poolInformation[_pid].offeringAmountPool.mul(allocation).div(1e12);

            // Calculate the payAmount
            uint256 payAmount = _poolInformation[_pid].raisingAmountPool.mul(allocation).div(1e12).add(1);

            // Calculate the pre-tax refunding amount
            userRefundingAmount = _userInfo[_user][_pid].amountPool.sub(payAmount);

            // Retrieve the tax rate
            if (_poolInformation[_pid].hasTax) {
                uint256 taxOverflow = _calculateTaxOverflow(
                    _poolInformation[_pid].totalAmountPool,
                    _poolInformation[_pid].raisingAmountPool
                );

                // Calculate the final taxAmount
                taxAmount = userRefundingAmount.mul(taxOverflow).div(1e12);

                // Adjust the refunding amount
                userRefundingAmount = userRefundingAmount.sub(taxAmount);
            }
        } else {
            userRefundingAmount = 0;
            taxAmount = 0;
            // _userInfo[_user] / (raisingAmount / offeringAmount)
            userOfferingAmount = _userInfo[_user][_pid].amountPool.mul(_poolInformation[_pid].offeringAmountPool).div(
                _poolInformation[_pid].raisingAmountPool
            );
        }
        return (userOfferingAmount, userRefundingAmount, taxAmount);
    }

    /**
     * @notice It returns the user allocation for pool
     * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)
     * @param _user: user address
     * @param _pid: pool id
     * @return it returns the user's share of pool
     */
    function _getUserAllocationPool(address _user, uint8 _pid) internal view returns (uint256) {
        if (_poolInformation[_pid].totalAmountPool > 0) {
            return _userInfo[_user][_pid].amountPool.mul(1e18).div(_poolInformation[_pid].totalAmountPool.mul(1e6));
        } else {
            return 0;
        }
    }

    /**
     * @notice Check if an address is a contract
     */
    function _isContract(address _addr) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(_addr)
        }
        return size > 0;
    }

    /**
     * @dev Checks if account is whitelisted
     * @param _pid: poolId
     * @param _account: The address to check
     */
    function _checkWhitelistStatus(uint8 _pid, address _account) internal view returns (bool) {
        address whitelistCheckerAddress = _poolInformation[_pid].whitelister;
        return (whitelistCheckerAddress == address(0)) || IWhitelistable(whitelistCheckerAddress).isWhitelisted(_account);
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"AdminTokenRecovery","inputs":[{"type":"address","name":"tokenAddress","internalType":"address","indexed":false},{"type":"uint256","name":"amountTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AdminWithdraw","inputs":[{"type":"uint256","name":"amountLP","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOfferingToken","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint8","name":"pid","internalType":"uint8","indexed":true}],"anonymous":false},{"type":"event","name":"Harvest","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"offeringAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"excessAmount","internalType":"uint256","indexed":false},{"type":"uint8","name":"pid","internalType":"uint8","indexed":true}],"anonymous":false},{"type":"event","name":"NewHarvestBlocks","inputs":[{"type":"uint256","name":"harvestBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewStartAndEndBlocks","inputs":[{"type":"uint256","name":"startBlock","internalType":"uint256","indexed":false},{"type":"uint256","name":"endBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PointParametersSet","inputs":[{"type":"uint256","name":"campaignId","internalType":"uint256","indexed":false},{"type":"uint256","name":"numberPoints","internalType":"uint256","indexed":false},{"type":"uint256","name":"thresholdPoints","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PoolParametersSet","inputs":[{"type":"uint256","name":"offeringAmountPool","internalType":"uint256","indexed":false},{"type":"uint256","name":"raisingAmountPool","internalType":"uint256","indexed":false},{"type":"uint8","name":"pid","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"IFO_FACTORY","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_BUFFER_BLOCKS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"NUMBER_POOLS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"campaignId","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"depositPool","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"},{"type":"uint8","name":"_pid","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"endBlock","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"finalWithdraw","inputs":[{"type":"uint256","name":"_lpAmount","internalType":"uint256"},{"type":"uint256","name":"_offerAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"harvestBlock","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"harvestPool","inputs":[{"type":"uint8","name":"_pid","internalType":"uint8"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_lpToken","internalType":"address"},{"type":"address","name":"_offeringToken","internalType":"address"},{"type":"address","name":"_mojitoProfileAddress","internalType":"address"},{"type":"uint256","name":"_startBlock","internalType":"uint256"},{"type":"uint256","name":"_endBlock","internalType":"uint256"},{"type":"uint256","name":"_maxBufferBlocks","internalType":"uint256"},{"type":"address","name":"_adminAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInitialized","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IKRC20"}],"name":"lpToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract MojitoProfile"}],"name":"mojitoProfile","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"numberPoints","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IKRC20"}],"name":"offeringToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverWrongTokens","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPool","inputs":[{"type":"uint256","name":"_offeringAmountPool","internalType":"uint256"},{"type":"uint256","name":"_raisingAmountPool","internalType":"uint256"},{"type":"uint256","name":"_limitPerUserInLP","internalType":"uint256"},{"type":"bool","name":"_hasTax","internalType":"bool"},{"type":"uint8","name":"_pid","internalType":"uint8"},{"type":"address","name":"_whitelister","internalType":"address"},{"type":"address","name":"_vester","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"startBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"thresholdPoints","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalTokensOffered","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateHarvestBlocks","inputs":[{"type":"uint256","name":"_harvestBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updatePointParameters","inputs":[{"type":"uint256","name":"_campaignId","internalType":"uint256"},{"type":"uint256","name":"_numberPoints","internalType":"uint256"},{"type":"uint256","name":"_thresholdPoints","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateStartAndEndBlocks","inputs":[{"type":"uint256","name":"_startBlock","internalType":"uint256"},{"type":"uint256","name":"_endBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct IIFOV2.PoolCharacteristics","components":[{"type":"uint256","name":"raisingAmountPool","internalType":"uint256"},{"type":"uint256","name":"offeringAmountPool","internalType":"uint256"},{"type":"uint256","name":"limitPerUserInLP","internalType":"uint256"},{"type":"bool","name":"hasTax","internalType":"bool"},{"type":"uint256","name":"totalAmountPool","internalType":"uint256"},{"type":"uint256","name":"sumTaxesOverflow","internalType":"uint256"},{"type":"address","name":"whitelister","internalType":"address"},{"type":"address","name":"vester","internalType":"address"}]}],"name":"viewPoolInformation","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"viewPoolTaxRateOverflow","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"viewUserAllocationPools","inputs":[{"type":"address","name":"_user","internalType":"address"},{"type":"uint8[]","name":"_pids","internalType":"uint8[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"},{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"viewUserInfo","inputs":[{"type":"address","name":"_user","internalType":"address"},{"type":"uint8[]","name":"_pids","internalType":"uint8[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[3][]","name":"","internalType":"uint256[3][]"}],"name":"viewUserOfferingAndRefundingAmountsForPools","inputs":[{"type":"address","name":"_user","internalType":"address"},{"type":"uint8[]","name":"_pids","internalType":"uint8[]"}]}]
              

Contract Creation Code

Verify & Publish
0x60a060405234801561001057600080fd5b506001600090815561002061007a565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3503360601b60805261007e565b3390565b60805160601c613310620000a16000398061091b52806110d752506133106000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c8063760b31801161010f5780639f1b5248116100a2578063f076b0ad11610071578063f076b0ad146103ad578063f2fde38b146103b5578063f7b119a9146103c8578063f9cd5c12146103db576101e5565b80639f1b52481461036a578063a83e79751461037d578063b781360714610385578063ca463ca41461038d576101e5565b80638da5cb5b116100de5780638da5cb5b1461033f5780638ed5b0fc14610347578063915dfc311461034f5780639513997f14610357576101e5565b8063760b3180146102fe5780637edd5e34146103065780637f1bdd76146103195780638754bbc61461032c576101e5565b80633f138d4b1161018757806349681dad1161015657806349681dad146102c55780634af3c9b7146102cd5780635fcbd285146102ee578063715018a6146102f6576101e5565b80633f138d4b1461028257806345de0f771461029557806346ab91bf1461029d57806348cd4cb1146102bd576101e5565b806323f93574116101c357806323f93574146102325780632ee520d01461024757806337f859b81461025a578063392e53cd1461026d576101e5565b806306acd65f146101ea578063083c6323146102085780632374876c1461021d575b600080fd5b6101f26103fb565b6040516101ff9190612437565b60405180910390f35b61021061040a565b6040516101ff919061320c565b61023061022b3660046123c6565b610410565b005b61023a6106fa565b6040516101ff9190613252565b6102306102553660046122af565b6106ff565b6102106102683660046122af565b6107c0565b61027561081d565b6040516101ff919061257c565b610230610290366004612269565b61082d565b6101f2610919565b6102b06102ab3660046122af565b61093d565b6040516101ff91906131a7565b6102106109cd565b6102106109d3565b6102e06102db3660046121e8565b6109d9565b6040516101ff929190612524565b6101f2610b1a565b610230610b29565b610210610bb2565b610230610314366004612300565b610bb8565b610230610327366004612392565b610c68565b61023061033a366004612174565b6110a2565b6101f2611180565b61021061118f565b610210611195565b6102306103653660046122df565b61119b565b6102306103783660046122df565b611298565b61021061147f565b6101f2611485565b6103a061039b3660046121e8565b611494565b6040516101ff91906124a9565b6102106115bd565b6102306103c3366004612159565b6115c3565b6102306103d636600461232b565b611684565b6103ee6103e93660046121e8565b611890565b6040516101ff9190612511565b6005546001600160a01b031681565b60075481565b6002600054141561043c5760405162461bcd60e51b815260040161043390613016565b60405180910390fd5b600260005561044a33611934565b156104675760405162461bcd60e51b81526004016104339061266d565b3332146104865760405162461bcd60e51b815260040161043390612ddd565b60085443116104a75760405162461bcd60e51b815260040161043390612cfb565b600260ff8216106104ca5760405162461bcd60e51b815260040161043390612ba8565b336000908152601e6020908152604080832060ff851684529091529020546105045760405162461bcd60e51b815260040161043390612aab565b336000908152601e6020908152604080832060ff808616855292529091206001015416156105445760405162461bcd60e51b815260040161043390612f81565b61054d3361193a565b336000818152601e6020908152604080832060ff8616845290915281206001908101805460ff1916909117905590819081906105899085611a36565b9194509250905080156105dc576105c081600d8660ff16600281106105aa57fe5b6008020160050154611c1690919063ffffffff16565b600d8560ff16600281106105d057fe5b60080201600501819055505b821561068b576000600d8560ff16600281106105f457fe5b60080201600701546001600160a01b031690508061062857600454610623906001600160a01b03163386611c44565b610689565b604051637d0dabb560e01b81526001600160a01b03821690637d0dabb590610656903390889060040161244b565b600060405180830381600087803b15801561067057600080fd5b505af1158015610684573d6000803e3d6000fd5b505050505b505b81156106a8576003546106a8906001600160a01b03163384611c44565b8360ff16336001600160a01b03167f51524c2e5edfedf8b01b29719c661e4fbe27e71734e7cd773dabb7cb712fb3b385856040516106e7929190613215565b60405180910390a3505060016000555050565b600281565b610707611c9f565b6001600160a01b0316610718611180565b6001600160a01b03161461073e5760405162461bcd60e51b815260040161043390612b73565b600654431061075f5760405162461bcd60e51b8152600401610433906130f4565b80600754106107805760405162461bcd60e51b815260040161043390612f07565b60088190556040517fb22cba2fc2aea10ed7f6b831e399a560a482f697c3d97de952482224da8bf845906107b590839061320c565b60405180910390a150565b6000600d82600281106107cf57fe5b600802016003015460ff166107e657506000610818565b610815600d83600281106107f657fe5b6008020160040154600d846002811061080b57fe5b6008020154611ca3565b90505b919050565b600554600160a01b900460ff1681565b610835611c9f565b6001600160a01b0316610846611180565b6001600160a01b03161461086c5760405162461bcd60e51b815260040161043390612b73565b6003546001600160a01b038381169116141561089a5760405162461bcd60e51b8152600401610433906128bc565b6004546001600160a01b03838116911614156108c85760405162461bcd60e51b815260040161043390612610565b6108dc6001600160a01b0383163383611c44565b7f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129828260405161090d92919061244b565b60405180910390a15050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6109456120ba565b600d826002811061095257fe5b6040805161010081018252600892909202929092018054825260018101546020830152600281015492820192909252600382015460ff161515606082015260048201546080820152600582015460a082015260068201546001600160a01b0390811660c083015260079092015490911660e082015292915050565b60065481565b600a5481565b606080808367ffffffffffffffff811180156109f457600080fd5b50604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50905060608467ffffffffffffffff81118015610a3a57600080fd5b50604051908082528060200260200182016040528015610a64578160200160208202803683370190505b50905060005b600260ff82161015610b0d576001600160a01b0388166000908152601e6020908152604080832060ff85168085529252909120548451909185918110610aac57fe5b6020908102919091018101919091526001600160a01b0389166000908152601e8252604080822060ff8086168085529190945291206001015484519216918491908110610af557fe5b91151560209283029190910190910152600101610a6a565b5090969095509350505050565b6003546001600160a01b031681565b610b31611c9f565b6001600160a01b0316610b42611180565b6001600160a01b031614610b685760405162461bcd60e51b815260040161043390612b73565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600c5481565b610bc0611c9f565b6001600160a01b0316610bd1611180565b6001600160a01b031614610bf75760405162461bcd60e51b815260040161043390612b73565b6007544310610c185760405162461bcd60e51b8152600401610433906125ba565b600a8290556009839055600b8190556040517f2058a318dbdfd2edd92a32cfa0ee233a30b165b83b421830109cb22ae86f674590610c5b90859085908590613223565b60405180910390a1505050565b60026000541415610c8b5760405162461bcd60e51b815260040161043390613016565b6002600055610c9933611934565b15610cb65760405162461bcd60e51b81526004016104339061266d565b333214610cd55760405162461bcd60e51b815260040161043390612ddd565b60055460405163ea0d5dcd60e01b81526001600160a01b039091169063ea0d5dcd90610d05903390600401612437565b60206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d559190612293565b610d715760405162461bcd60e51b8152600401610433906127df565b600260ff821610610d945760405162461bcd60e51b815260040161043390612d43565b610d9e8133611d42565b610dba5760405162461bcd60e51b81526004016104339061273d565b6000600d8260ff1660028110610dcc57fe5b6008020160010154118015610df557506000600d8260ff1660028110610dee57fe5b6008020154115b610e115760405162461bcd60e51b815260040161043390612794565b6006544311610e325760405162461bcd60e51b81526004016104339061304d565b6007544310610e535760405162461bcd60e51b815260040161043390612afd565b60008211610e735760405162461bcd60e51b8152600401610433906129e9565b6000600d8260ff1660028110610e8557fe5b60080201600701546001600160a01b0316905080610f3d57600c54600480546040516370a0823160e01b81526001600160a01b03909116916370a0823191610ecf91309101612437565b60206040518083038186803b158015610ee757600080fd5b505afa158015610efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1f91906122c7565b1015610f3d5760405162461bcd60e51b81526004016104339061299e565b600354610f55906001600160a01b0316333086611df4565b336000908152601e6020908152604080832060ff86168452909152902054610f7d9084611c16565b336000908152601e6020908152604080832060ff8716808552925282209290925590600d9060028110610fac57fe5b6008020160020154111561100e57600d8260ff1660028110610fca57fe5b6008020160020154336000908152601e6020908152604080832060ff87168452909152902054111561100e5760405162461bcd60e51b81526004016104339061314a565b61103883600d8460ff166002811061102257fe5b6008020160040154611c1690919063ffffffff16565b600d8360ff166002811061104857fe5b60080201600401819055508160ff16336001600160a01b03167ff763e680fce25a97ffd55d8b705370c98b47b2285f7b3b2900c43606fd41804585604051611090919061320c565b60405180910390a35050600160005550565b600554600160a01b900460ff16156110cc5760405162461bcd60e51b815260040161043390612c4d565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111145760405162461bcd60e51b81526004016104339061283c565b60058054600380546001600160a01b03808c166001600160a01b031992831617909255600480548b841690831617905590881660ff60a01b19909216600160a01b17161790556006849055600783905560088390556002829055611177816115c3565b50505050505050565b6001546001600160a01b031690565b60095481565b60025481565b6111a3611c9f565b6001600160a01b03166111b4611180565b6001600160a01b0316146111da5760405162461bcd60e51b815260040161043390612b73565b600254430181106111fd5760405162461bcd60e51b815260040161043390612ebc565b600654431061121e5760405162461bcd60e51b815260040161043390612e3a565b80821061123d5760405162461bcd60e51b815260040161043390613083565b81431061125c5760405162461bcd60e51b815260040161043390612a3a565b600682905560078190556040517f7cd0ab87d19036f3dfadadb232c78aa4879dda3f0c994a9d637532410ee2ce069061090d9084908490613215565b6112a0611c9f565b6001600160a01b03166112b1611180565b6001600160a01b0316146112d75760405162461bcd60e51b815260040161043390612b73565b6003546040516370a0823160e01b81526001600160a01b03909116906370a0823190611307903090600401612437565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135791906122c7565b8211156113765760405162461bcd60e51b815260040161043390612bf8565b600480546040516370a0823160e01b81526001600160a01b03909116916370a08231916113a591309101612437565b60206040518083038186803b1580156113bd57600080fd5b505afa1580156113d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f591906122c7565b8111156114145760405162461bcd60e51b815260040161043390612c9e565b811561143157600354611431906001600160a01b03163384611c44565b801561144e5760045461144e906001600160a01b03163383611c44565b7f94ebb62a252249c867ecb758d386f50a95be7e8df9e1c52917c9cf494327dd7d828260405161090d929190613215565b600b5481565b6004546001600160a01b031681565b6060808267ffffffffffffffff811180156114ae57600080fd5b506040519080825280602002602001820160405280156114e857816020015b6114d5612113565b8152602001906001900390816114cd5790505b50905060005b60ff81168411156115b257600080600080600d89898760ff1681811061151057fe5b905060200201602081019061152591906123c6565b60ff166002811061153257fe5b600802015411156115725761156a8989898760ff1681811061155057fe5b905060200201602081019061156591906123c6565b611a36565b919450925090505b604051806060016040528084815260200183815260200182815250858560ff168151811061159c57fe5b60209081029190910101525050506001016114ee565b5090505b9392505050565b60085481565b6115cb611c9f565b6001600160a01b03166115dc611180565b6001600160a01b0316146116025760405162461bcd60e51b815260040161043390612b73565b6001600160a01b0381166116285760405162461bcd60e51b8152600401610433906126c0565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b61168c611c9f565b6001600160a01b031661169d611180565b6001600160a01b0316146116c35760405162461bcd60e51b815260040161043390612b73565b60065443106116e45760405162461bcd60e51b815260040161043390612d93565b600260ff8416106117075760405162461bcd60e51b815260040161043390612950565b86600d8460ff166002811061171857fe5b600802016001018190555085600d8460ff166002811061173457fe5b600802015584600d60ff85166002811061174a57fe5b600802016002018190555083600d8460ff166002811061176657fe5b6008020160030160006101000a81548160ff02191690831515021790555081600d8460ff166002811061179557fe5b6008020160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080600d8460ff16600281106117d157fe5b6008020160070160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600080600090505b600260ff821610156118445761183a600d8260ff166002811061182357fe5b600802016001015483611c1690919063ffffffff16565b9150600101611804565b50600c8190556040517fddaf243a142670be60c19ff7116b5d8b124717b29bb4cc03cead42161614105b9061187e908a908a908890613239565b60405180910390a15050505050505050565b6060808267ffffffffffffffff811180156118aa57600080fd5b506040519080825280602002602001820160405280156118d4578160200160208202803683370190505b50905060005b60ff81168411156115b2576119128686868460ff168181106118f857fe5b905060200201602081019061190d91906123c6565b611e1b565b828260ff168151811061192157fe5b60209081029190910101526001016118da565b3b151590565b6001600160a01b0381166000908152601d602052604090205460ff16611a33576000805b600260ff8216101561199d57336000908152601e6020908152604080832060ff85168452909152902054611993908390611c16565b915060010161195e565b50600b54811115611a31576001600160a01b038083166000908152601d602052604090819020805460ff19166001179055600554600a546009549251630dee0bfb60e11b81529190931692631bdc17f6926119fe9233929190600401612464565b600060405180830381600087803b158015611a1857600080fd5b505af1158015611a2c573d6000803e3d6000fd5b505050505b505b50565b600080600080600080600d8760ff1660028110611a4f57fe5b6008020154600d60ff891660028110611a6457fe5b60080201600401541115611b9f576000611a7e8989611e1b565b9050611ab964e8d4a51000611ab383600d8c60ff1660028110611a9d57fe5b6008020160010154611eb490919063ffffffff16565b90611eee565b93506000611af06001611aea64e8d4a51000611ab386600d8f60ff1660028110611adf57fe5b600802015490611eb4565b90611c16565b6001600160a01b038b166000908152601e6020908152604080832060ff8e168452909152902054909150611b249082611f20565b9350600d8960ff1660028110611b3657fe5b600802016003015460ff1615611b98576000611b73600d8b60ff1660028110611b5b57fe5b6008020160040154600d8c60ff166002811061080b57fe5b9050611b8864e8d4a51000611ab38784611eb4565b9350611b948585611f20565b9450505b5050611c08565b506000905080611c05600d60ff891660028110611bb857fe5b6008020154611ab3600d60ff8b1660028110611bd057fe5b60080201600101546001600160a01b038c166000908152601e6020908152604080832060ff8f16845290915290205490611eb4565b92505b919450925090509250925092565b600082820183811015611c3b5760405162461bcd60e51b815260040161043390612706565b90505b92915050565b611c9a8363a9059cbb60e01b8484604051602401611c6392919061244b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f48565b505050565b3390565b600080611cb08484611eee565b90506105dc8110611cc857631dcd6500915050611c3e565b6103e88110611cde57633b9aca00915050611c3e565b6101f48110611cf4576377359400915050611c3e565b60fa8110611d0957639502f900915050611c3e565b60648110611d1e5763b2d05e00915050611c3e565b60328110611d345764012a05f200915050611c3e565b6402540be400915050611c3e565b600080600d8460ff1660028110611d5557fe5b60080201600601546001600160a01b03169050801580611dec5750604051633af32abf60e01b81526001600160a01b03821690633af32abf90611d9c908690600401612437565b60206040518083038186803b158015611db457600080fd5b505afa158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190612293565b949350505050565b611e15846323b872dd60e01b858585604051602401611c6393929190612485565b50505050565b600080600d8360ff1660028110611e2e57fe5b60080201600401541115611eac57611ea5611e6c620f4240600d8560ff1660028110611e5657fe5b6008020160040154611eb490919063ffffffff16565b6001600160a01b0385166000908152601e6020908152604080832060ff88168452909152902054611ab390670de0b6b3a7640000611eb4565b9050611c3e565b506000611c3e565b600082611ec357506000611c3e565b82820282848281611ed057fe5b0414611c3b5760405162461bcd60e51b815260040161043390612b32565b6000808211611f0f5760405162461bcd60e51b815260040161043390612919565b818381611f1857fe5b049392505050565b600082821115611f425760405162461bcd60e51b815260040161043390612885565b50900390565b6060611f9d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fd79092919063ffffffff16565b805190915015611c9a5780806020019051810190611fbb9190612293565b611c9a5760405162461bcd60e51b815260040161043390612fcc565b6060611dec848460008585611feb85611934565b6120075760405162461bcd60e51b815260040161043390612e85565b60006060866001600160a01b03168587604051612024919061241b565b60006040518083038185875af1925050503d8060008114612061576040519150601f19603f3d011682016040523d82523d6000602084013e612066565b606091505b5091509150612076828286612081565b979650505050505050565b606083156120905750816115b6565b8251156120a05782518084602001fd5b8160405162461bcd60e51b81526004016104339190612587565b604051806101000160405280600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b60405180606001604052806003906020820280368337509192915050565b80356001600160a01b0381168114611c3e57600080fd5b803560ff81168114611c3e57600080fd5b60006020828403121561216a578081fd5b6115b68383612131565b600080600080600080600060e0888a03121561218e578283fd5b6121988989612131565b96506121a78960208a01612131565b95506121b68960408a01612131565b9450606088013593506080880135925060a088013591506121da8960c08a01612131565b905092959891949750929550565b6000806000604084860312156121fc578283fd5b6122068585612131565b9250602084013567ffffffffffffffff80821115612222578384fd5b818601915086601f830112612235578384fd5b813581811115612243578485fd5b8760208083028501011115612256578485fd5b6020830194508093505050509250925092565b6000806040838503121561227b578182fd5b6122858484612131565b946020939093013593505050565b6000602082840312156122a4578081fd5b8151611c3b8161328c565b6000602082840312156122c0578081fd5b5035919050565b6000602082840312156122d8578081fd5b5051919050565b600080604083850312156122f1578182fd5b50508035926020909101359150565b600080600060608486031215612314578283fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215612345578283fd5b87359650602088013595506040880135945060608801356123658161328c565b93506123748960808a01612148565b92506123838960a08a01612131565b91506121da8960c08a01612131565b600080604083850312156123a4578182fd5b82359150602083013560ff811681146123bb578182fd5b809150509250929050565b6000602082840312156123d7578081fd5b6115b68383612148565b6000815180845260208085019450808401835b83811015612410578151875295820195908201906001016123f4565b509495945050505050565b6000825161242d818460208701613260565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252825182820181905260009190848201906040850190845b8181101561250557835183875b60038110156124ef578251825291870191908701906001016124d2565b50505092840192606092909201916001016124c5565b50909695505050505050565b6000602082526115b660208301846123e1565b60006040825261253760408301856123e1565b828103602084810191909152845180835285820192820190845b8181101561256f578451151583529383019391830191600101612551565b5090979650505050505050565b901515815260200190565b60006020825282518060208401526125a6816040850160208701613260565b601f01601f19169190910160400192915050565b60208082526036908201527f49464f496e697469616c697a61626c653a3a757064617465506f696e74506172604082015275185b595d195c9cce88125193c81a185cc8195b99195960521b606082015260800190565b6020808252603e908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265206f66666572696e6720746f6b656e0000606082015260800190565b60208082526033908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a20436040820152721bdb9d1c9858dd081b9bdd08185b1b1bddd959606a1b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260439082015260008051602061329b83398151915260408201527f6869732061646472657373206973206e6f7420696e207468652077686974656c6060820152621a5cdd60ea1b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205060408201526a1bdbdb081b9bdd081cd95d60aa1b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204d60408201527f757374206861766520616e206163746976652070726f66696c65000000000000606082015260800190565b60208082526029908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a204e6f6040820152687420666163746f727960b81b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526038908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265204c5020746f6b656e0000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252602e908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a20506f6f6c2060408201526d191bd95cc81b9bdd08195e1a5cdd60921b606082015260800190565b6020808252603c9082015260008051602061329b83398151915260408201527f6f6b656e73206e6f74206465706f73697465642070726f7065726c7900000000606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204160408201527006d6f756e74206d757374206265203e203607c1b606082015260800190565b6020808252605b908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f6520686967686572207468616e2063757272656e7420626c6f636b0000000000608082015260a00190565b60208082526032908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20446040820152716964206e6f7420706172746963697061746560701b606082015260800190565b602080825260279082015260008051602061329b8339815191526040820152666f6f206c61746560c81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b60208082526035908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a604082015274204e6f7420656e6f756768204c5020746f6b656e7360581b606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a20416c6040820152701c9958591e481a5b9a5d1a585b1a5e9959607a1b606082015260800190565b6020808252603b908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a60408201527f204e6f7420656e6f756768206f66666572696e6720746f6b656e730000000000606082015260800190565b60208082526028908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20546040820152676f6f206561726c7960c01b606082015260800190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b6020808252602a908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a2049464f2068604082015269185cc81cdd185c9d195960b21b606082015260800190565b60208082526039908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a205060408201527f726f787920636f6e7472616374206e6f7420616c6c6f77656400000000000000606082015260800190565b6020808252603a908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a2049464f206861732073746172746564000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252603b908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a20456e64426c6f636b20746f6f206661720000000000606082015260800190565b60208082526054908201527f49464f496e697469616c697a61626c653a3a757064617465486172766573744260408201527f6c6f636b733a204e65772068617276657374426c6f636b206d75737420626520606082015273686967686572207468616e20656e64426c6f636b60601b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204160408201526a6c726561647920646f6e6560a81b606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260289082015260008051602061329b8339815191526040820152676f6f206561726c7960c01b606082015260800190565b60208082526059908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f65206c6f776572207468616e206e657720656e64426c6f636b00000000000000608082015260a00190565b60208082526036908201527f49464f496e697469616c697a61626c653a3a75706461746548617276657374426040820152751b1bd8dadcce88125193c81a185cc81cdd185c9d195960521b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201527f657720616d6f756e742061626f76652075736572206c696d6974000000000000606082015260800190565b8151815260208083015190820152604080830151908201526060808301511515908201526080808301519082015260a0808301519082015260c0808301516001600160a01b039081169183019190915260e09283015116918101919091526101000190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b928352602083019190915260ff16604082015260600190565b60ff91909116815260200190565b60005b8381101561327b578181015183820152602001613263565b83811115611e155750506000910152565b8015158114611a3357600080fdfe49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205449464f496e697469616c697a61626c653a3a7570646174655374617274416e64a26469706673582212201d88ad7a7d7b30bf04f652ff2f487b0b02787e400af603a73122fa886a90fafd64736f6c634300060c0033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101e55760003560e01c8063760b31801161010f5780639f1b5248116100a2578063f076b0ad11610071578063f076b0ad146103ad578063f2fde38b146103b5578063f7b119a9146103c8578063f9cd5c12146103db576101e5565b80639f1b52481461036a578063a83e79751461037d578063b781360714610385578063ca463ca41461038d576101e5565b80638da5cb5b116100de5780638da5cb5b1461033f5780638ed5b0fc14610347578063915dfc311461034f5780639513997f14610357576101e5565b8063760b3180146102fe5780637edd5e34146103065780637f1bdd76146103195780638754bbc61461032c576101e5565b80633f138d4b1161018757806349681dad1161015657806349681dad146102c55780634af3c9b7146102cd5780635fcbd285146102ee578063715018a6146102f6576101e5565b80633f138d4b1461028257806345de0f771461029557806346ab91bf1461029d57806348cd4cb1146102bd576101e5565b806323f93574116101c357806323f93574146102325780632ee520d01461024757806337f859b81461025a578063392e53cd1461026d576101e5565b806306acd65f146101ea578063083c6323146102085780632374876c1461021d575b600080fd5b6101f26103fb565b6040516101ff9190612437565b60405180910390f35b61021061040a565b6040516101ff919061320c565b61023061022b3660046123c6565b610410565b005b61023a6106fa565b6040516101ff9190613252565b6102306102553660046122af565b6106ff565b6102106102683660046122af565b6107c0565b61027561081d565b6040516101ff919061257c565b610230610290366004612269565b61082d565b6101f2610919565b6102b06102ab3660046122af565b61093d565b6040516101ff91906131a7565b6102106109cd565b6102106109d3565b6102e06102db3660046121e8565b6109d9565b6040516101ff929190612524565b6101f2610b1a565b610230610b29565b610210610bb2565b610230610314366004612300565b610bb8565b610230610327366004612392565b610c68565b61023061033a366004612174565b6110a2565b6101f2611180565b61021061118f565b610210611195565b6102306103653660046122df565b61119b565b6102306103783660046122df565b611298565b61021061147f565b6101f2611485565b6103a061039b3660046121e8565b611494565b6040516101ff91906124a9565b6102106115bd565b6102306103c3366004612159565b6115c3565b6102306103d636600461232b565b611684565b6103ee6103e93660046121e8565b611890565b6040516101ff9190612511565b6005546001600160a01b031681565b60075481565b6002600054141561043c5760405162461bcd60e51b815260040161043390613016565b60405180910390fd5b600260005561044a33611934565b156104675760405162461bcd60e51b81526004016104339061266d565b3332146104865760405162461bcd60e51b815260040161043390612ddd565b60085443116104a75760405162461bcd60e51b815260040161043390612cfb565b600260ff8216106104ca5760405162461bcd60e51b815260040161043390612ba8565b336000908152601e6020908152604080832060ff851684529091529020546105045760405162461bcd60e51b815260040161043390612aab565b336000908152601e6020908152604080832060ff808616855292529091206001015416156105445760405162461bcd60e51b815260040161043390612f81565b61054d3361193a565b336000818152601e6020908152604080832060ff8616845290915281206001908101805460ff1916909117905590819081906105899085611a36565b9194509250905080156105dc576105c081600d8660ff16600281106105aa57fe5b6008020160050154611c1690919063ffffffff16565b600d8560ff16600281106105d057fe5b60080201600501819055505b821561068b576000600d8560ff16600281106105f457fe5b60080201600701546001600160a01b031690508061062857600454610623906001600160a01b03163386611c44565b610689565b604051637d0dabb560e01b81526001600160a01b03821690637d0dabb590610656903390889060040161244b565b600060405180830381600087803b15801561067057600080fd5b505af1158015610684573d6000803e3d6000fd5b505050505b505b81156106a8576003546106a8906001600160a01b03163384611c44565b8360ff16336001600160a01b03167f51524c2e5edfedf8b01b29719c661e4fbe27e71734e7cd773dabb7cb712fb3b385856040516106e7929190613215565b60405180910390a3505060016000555050565b600281565b610707611c9f565b6001600160a01b0316610718611180565b6001600160a01b03161461073e5760405162461bcd60e51b815260040161043390612b73565b600654431061075f5760405162461bcd60e51b8152600401610433906130f4565b80600754106107805760405162461bcd60e51b815260040161043390612f07565b60088190556040517fb22cba2fc2aea10ed7f6b831e399a560a482f697c3d97de952482224da8bf845906107b590839061320c565b60405180910390a150565b6000600d82600281106107cf57fe5b600802016003015460ff166107e657506000610818565b610815600d83600281106107f657fe5b6008020160040154600d846002811061080b57fe5b6008020154611ca3565b90505b919050565b600554600160a01b900460ff1681565b610835611c9f565b6001600160a01b0316610846611180565b6001600160a01b03161461086c5760405162461bcd60e51b815260040161043390612b73565b6003546001600160a01b038381169116141561089a5760405162461bcd60e51b8152600401610433906128bc565b6004546001600160a01b03838116911614156108c85760405162461bcd60e51b815260040161043390612610565b6108dc6001600160a01b0383163383611c44565b7f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129828260405161090d92919061244b565b60405180910390a15050565b7f00000000000000000000000009df353efd36027de6c15fc8a1b4af7cd98f052181565b6109456120ba565b600d826002811061095257fe5b6040805161010081018252600892909202929092018054825260018101546020830152600281015492820192909252600382015460ff161515606082015260048201546080820152600582015460a082015260068201546001600160a01b0390811660c083015260079092015490911660e082015292915050565b60065481565b600a5481565b606080808367ffffffffffffffff811180156109f457600080fd5b50604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50905060608467ffffffffffffffff81118015610a3a57600080fd5b50604051908082528060200260200182016040528015610a64578160200160208202803683370190505b50905060005b600260ff82161015610b0d576001600160a01b0388166000908152601e6020908152604080832060ff85168085529252909120548451909185918110610aac57fe5b6020908102919091018101919091526001600160a01b0389166000908152601e8252604080822060ff8086168085529190945291206001015484519216918491908110610af557fe5b91151560209283029190910190910152600101610a6a565b5090969095509350505050565b6003546001600160a01b031681565b610b31611c9f565b6001600160a01b0316610b42611180565b6001600160a01b031614610b685760405162461bcd60e51b815260040161043390612b73565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600c5481565b610bc0611c9f565b6001600160a01b0316610bd1611180565b6001600160a01b031614610bf75760405162461bcd60e51b815260040161043390612b73565b6007544310610c185760405162461bcd60e51b8152600401610433906125ba565b600a8290556009839055600b8190556040517f2058a318dbdfd2edd92a32cfa0ee233a30b165b83b421830109cb22ae86f674590610c5b90859085908590613223565b60405180910390a1505050565b60026000541415610c8b5760405162461bcd60e51b815260040161043390613016565b6002600055610c9933611934565b15610cb65760405162461bcd60e51b81526004016104339061266d565b333214610cd55760405162461bcd60e51b815260040161043390612ddd565b60055460405163ea0d5dcd60e01b81526001600160a01b039091169063ea0d5dcd90610d05903390600401612437565b60206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d559190612293565b610d715760405162461bcd60e51b8152600401610433906127df565b600260ff821610610d945760405162461bcd60e51b815260040161043390612d43565b610d9e8133611d42565b610dba5760405162461bcd60e51b81526004016104339061273d565b6000600d8260ff1660028110610dcc57fe5b6008020160010154118015610df557506000600d8260ff1660028110610dee57fe5b6008020154115b610e115760405162461bcd60e51b815260040161043390612794565b6006544311610e325760405162461bcd60e51b81526004016104339061304d565b6007544310610e535760405162461bcd60e51b815260040161043390612afd565b60008211610e735760405162461bcd60e51b8152600401610433906129e9565b6000600d8260ff1660028110610e8557fe5b60080201600701546001600160a01b0316905080610f3d57600c54600480546040516370a0823160e01b81526001600160a01b03909116916370a0823191610ecf91309101612437565b60206040518083038186803b158015610ee757600080fd5b505afa158015610efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1f91906122c7565b1015610f3d5760405162461bcd60e51b81526004016104339061299e565b600354610f55906001600160a01b0316333086611df4565b336000908152601e6020908152604080832060ff86168452909152902054610f7d9084611c16565b336000908152601e6020908152604080832060ff8716808552925282209290925590600d9060028110610fac57fe5b6008020160020154111561100e57600d8260ff1660028110610fca57fe5b6008020160020154336000908152601e6020908152604080832060ff87168452909152902054111561100e5760405162461bcd60e51b81526004016104339061314a565b61103883600d8460ff166002811061102257fe5b6008020160040154611c1690919063ffffffff16565b600d8360ff166002811061104857fe5b60080201600401819055508160ff16336001600160a01b03167ff763e680fce25a97ffd55d8b705370c98b47b2285f7b3b2900c43606fd41804585604051611090919061320c565b60405180910390a35050600160005550565b600554600160a01b900460ff16156110cc5760405162461bcd60e51b815260040161043390612c4d565b336001600160a01b037f00000000000000000000000009df353efd36027de6c15fc8a1b4af7cd98f052116146111145760405162461bcd60e51b81526004016104339061283c565b60058054600380546001600160a01b03808c166001600160a01b031992831617909255600480548b841690831617905590881660ff60a01b19909216600160a01b17161790556006849055600783905560088390556002829055611177816115c3565b50505050505050565b6001546001600160a01b031690565b60095481565b60025481565b6111a3611c9f565b6001600160a01b03166111b4611180565b6001600160a01b0316146111da5760405162461bcd60e51b815260040161043390612b73565b600254430181106111fd5760405162461bcd60e51b815260040161043390612ebc565b600654431061121e5760405162461bcd60e51b815260040161043390612e3a565b80821061123d5760405162461bcd60e51b815260040161043390613083565b81431061125c5760405162461bcd60e51b815260040161043390612a3a565b600682905560078190556040517f7cd0ab87d19036f3dfadadb232c78aa4879dda3f0c994a9d637532410ee2ce069061090d9084908490613215565b6112a0611c9f565b6001600160a01b03166112b1611180565b6001600160a01b0316146112d75760405162461bcd60e51b815260040161043390612b73565b6003546040516370a0823160e01b81526001600160a01b03909116906370a0823190611307903090600401612437565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135791906122c7565b8211156113765760405162461bcd60e51b815260040161043390612bf8565b600480546040516370a0823160e01b81526001600160a01b03909116916370a08231916113a591309101612437565b60206040518083038186803b1580156113bd57600080fd5b505afa1580156113d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f591906122c7565b8111156114145760405162461bcd60e51b815260040161043390612c9e565b811561143157600354611431906001600160a01b03163384611c44565b801561144e5760045461144e906001600160a01b03163383611c44565b7f94ebb62a252249c867ecb758d386f50a95be7e8df9e1c52917c9cf494327dd7d828260405161090d929190613215565b600b5481565b6004546001600160a01b031681565b6060808267ffffffffffffffff811180156114ae57600080fd5b506040519080825280602002602001820160405280156114e857816020015b6114d5612113565b8152602001906001900390816114cd5790505b50905060005b60ff81168411156115b257600080600080600d89898760ff1681811061151057fe5b905060200201602081019061152591906123c6565b60ff166002811061153257fe5b600802015411156115725761156a8989898760ff1681811061155057fe5b905060200201602081019061156591906123c6565b611a36565b919450925090505b604051806060016040528084815260200183815260200182815250858560ff168151811061159c57fe5b60209081029190910101525050506001016114ee565b5090505b9392505050565b60085481565b6115cb611c9f565b6001600160a01b03166115dc611180565b6001600160a01b0316146116025760405162461bcd60e51b815260040161043390612b73565b6001600160a01b0381166116285760405162461bcd60e51b8152600401610433906126c0565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b61168c611c9f565b6001600160a01b031661169d611180565b6001600160a01b0316146116c35760405162461bcd60e51b815260040161043390612b73565b60065443106116e45760405162461bcd60e51b815260040161043390612d93565b600260ff8416106117075760405162461bcd60e51b815260040161043390612950565b86600d8460ff166002811061171857fe5b600802016001018190555085600d8460ff166002811061173457fe5b600802015584600d60ff85166002811061174a57fe5b600802016002018190555083600d8460ff166002811061176657fe5b6008020160030160006101000a81548160ff02191690831515021790555081600d8460ff166002811061179557fe5b6008020160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080600d8460ff16600281106117d157fe5b6008020160070160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600080600090505b600260ff821610156118445761183a600d8260ff166002811061182357fe5b600802016001015483611c1690919063ffffffff16565b9150600101611804565b50600c8190556040517fddaf243a142670be60c19ff7116b5d8b124717b29bb4cc03cead42161614105b9061187e908a908a908890613239565b60405180910390a15050505050505050565b6060808267ffffffffffffffff811180156118aa57600080fd5b506040519080825280602002602001820160405280156118d4578160200160208202803683370190505b50905060005b60ff81168411156115b2576119128686868460ff168181106118f857fe5b905060200201602081019061190d91906123c6565b611e1b565b828260ff168151811061192157fe5b60209081029190910101526001016118da565b3b151590565b6001600160a01b0381166000908152601d602052604090205460ff16611a33576000805b600260ff8216101561199d57336000908152601e6020908152604080832060ff85168452909152902054611993908390611c16565b915060010161195e565b50600b54811115611a31576001600160a01b038083166000908152601d602052604090819020805460ff19166001179055600554600a546009549251630dee0bfb60e11b81529190931692631bdc17f6926119fe9233929190600401612464565b600060405180830381600087803b158015611a1857600080fd5b505af1158015611a2c573d6000803e3d6000fd5b505050505b505b50565b600080600080600080600d8760ff1660028110611a4f57fe5b6008020154600d60ff891660028110611a6457fe5b60080201600401541115611b9f576000611a7e8989611e1b565b9050611ab964e8d4a51000611ab383600d8c60ff1660028110611a9d57fe5b6008020160010154611eb490919063ffffffff16565b90611eee565b93506000611af06001611aea64e8d4a51000611ab386600d8f60ff1660028110611adf57fe5b600802015490611eb4565b90611c16565b6001600160a01b038b166000908152601e6020908152604080832060ff8e168452909152902054909150611b249082611f20565b9350600d8960ff1660028110611b3657fe5b600802016003015460ff1615611b98576000611b73600d8b60ff1660028110611b5b57fe5b6008020160040154600d8c60ff166002811061080b57fe5b9050611b8864e8d4a51000611ab38784611eb4565b9350611b948585611f20565b9450505b5050611c08565b506000905080611c05600d60ff891660028110611bb857fe5b6008020154611ab3600d60ff8b1660028110611bd057fe5b60080201600101546001600160a01b038c166000908152601e6020908152604080832060ff8f16845290915290205490611eb4565b92505b919450925090509250925092565b600082820183811015611c3b5760405162461bcd60e51b815260040161043390612706565b90505b92915050565b611c9a8363a9059cbb60e01b8484604051602401611c6392919061244b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f48565b505050565b3390565b600080611cb08484611eee565b90506105dc8110611cc857631dcd6500915050611c3e565b6103e88110611cde57633b9aca00915050611c3e565b6101f48110611cf4576377359400915050611c3e565b60fa8110611d0957639502f900915050611c3e565b60648110611d1e5763b2d05e00915050611c3e565b60328110611d345764012a05f200915050611c3e565b6402540be400915050611c3e565b600080600d8460ff1660028110611d5557fe5b60080201600601546001600160a01b03169050801580611dec5750604051633af32abf60e01b81526001600160a01b03821690633af32abf90611d9c908690600401612437565b60206040518083038186803b158015611db457600080fd5b505afa158015611dc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dec9190612293565b949350505050565b611e15846323b872dd60e01b858585604051602401611c6393929190612485565b50505050565b600080600d8360ff1660028110611e2e57fe5b60080201600401541115611eac57611ea5611e6c620f4240600d8560ff1660028110611e5657fe5b6008020160040154611eb490919063ffffffff16565b6001600160a01b0385166000908152601e6020908152604080832060ff88168452909152902054611ab390670de0b6b3a7640000611eb4565b9050611c3e565b506000611c3e565b600082611ec357506000611c3e565b82820282848281611ed057fe5b0414611c3b5760405162461bcd60e51b815260040161043390612b32565b6000808211611f0f5760405162461bcd60e51b815260040161043390612919565b818381611f1857fe5b049392505050565b600082821115611f425760405162461bcd60e51b815260040161043390612885565b50900390565b6060611f9d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fd79092919063ffffffff16565b805190915015611c9a5780806020019051810190611fbb9190612293565b611c9a5760405162461bcd60e51b815260040161043390612fcc565b6060611dec848460008585611feb85611934565b6120075760405162461bcd60e51b815260040161043390612e85565b60006060866001600160a01b03168587604051612024919061241b565b60006040518083038185875af1925050503d8060008114612061576040519150601f19603f3d011682016040523d82523d6000602084013e612066565b606091505b5091509150612076828286612081565b979650505050505050565b606083156120905750816115b6565b8251156120a05782518084602001fd5b8160405162461bcd60e51b81526004016104339190612587565b604051806101000160405280600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b60405180606001604052806003906020820280368337509192915050565b80356001600160a01b0381168114611c3e57600080fd5b803560ff81168114611c3e57600080fd5b60006020828403121561216a578081fd5b6115b68383612131565b600080600080600080600060e0888a03121561218e578283fd5b6121988989612131565b96506121a78960208a01612131565b95506121b68960408a01612131565b9450606088013593506080880135925060a088013591506121da8960c08a01612131565b905092959891949750929550565b6000806000604084860312156121fc578283fd5b6122068585612131565b9250602084013567ffffffffffffffff80821115612222578384fd5b818601915086601f830112612235578384fd5b813581811115612243578485fd5b8760208083028501011115612256578485fd5b6020830194508093505050509250925092565b6000806040838503121561227b578182fd5b6122858484612131565b946020939093013593505050565b6000602082840312156122a4578081fd5b8151611c3b8161328c565b6000602082840312156122c0578081fd5b5035919050565b6000602082840312156122d8578081fd5b5051919050565b600080604083850312156122f1578182fd5b50508035926020909101359150565b600080600060608486031215612314578283fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215612345578283fd5b87359650602088013595506040880135945060608801356123658161328c565b93506123748960808a01612148565b92506123838960a08a01612131565b91506121da8960c08a01612131565b600080604083850312156123a4578182fd5b82359150602083013560ff811681146123bb578182fd5b809150509250929050565b6000602082840312156123d7578081fd5b6115b68383612148565b6000815180845260208085019450808401835b83811015612410578151875295820195908201906001016123f4565b509495945050505050565b6000825161242d818460208701613260565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252825182820181905260009190848201906040850190845b8181101561250557835183875b60038110156124ef578251825291870191908701906001016124d2565b50505092840192606092909201916001016124c5565b50909695505050505050565b6000602082526115b660208301846123e1565b60006040825261253760408301856123e1565b828103602084810191909152845180835285820192820190845b8181101561256f578451151583529383019391830191600101612551565b5090979650505050505050565b901515815260200190565b60006020825282518060208401526125a6816040850160208701613260565b601f01601f19169190910160400192915050565b60208082526036908201527f49464f496e697469616c697a61626c653a3a757064617465506f696e74506172604082015275185b595d195c9cce88125193c81a185cc8195b99195960521b606082015260800190565b6020808252603e908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265206f66666572696e6720746f6b656e0000606082015260800190565b60208082526033908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a20436040820152721bdb9d1c9858dd081b9bdd08185b1b1bddd959606a1b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260439082015260008051602061329b83398151915260408201527f6869732061646472657373206973206e6f7420696e207468652077686974656c6060820152621a5cdd60ea1b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205060408201526a1bdbdb081b9bdd081cd95d60aa1b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204d60408201527f757374206861766520616e206163746976652070726f66696c65000000000000606082015260800190565b60208082526029908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a204e6f6040820152687420666163746f727960b81b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526038908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265204c5020746f6b656e0000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252602e908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a20506f6f6c2060408201526d191bd95cc81b9bdd08195e1a5cdd60921b606082015260800190565b6020808252603c9082015260008051602061329b83398151915260408201527f6f6b656e73206e6f74206465706f73697465642070726f7065726c7900000000606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204160408201527006d6f756e74206d757374206265203e203607c1b606082015260800190565b6020808252605b908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f6520686967686572207468616e2063757272656e7420626c6f636b0000000000608082015260a00190565b60208082526032908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20446040820152716964206e6f7420706172746963697061746560701b606082015260800190565b602080825260279082015260008051602061329b8339815191526040820152666f6f206c61746560c81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b60208082526035908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a604082015274204e6f7420656e6f756768204c5020746f6b656e7360581b606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a20416c6040820152701c9958591e481a5b9a5d1a585b1a5e9959607a1b606082015260800190565b6020808252603b908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a60408201527f204e6f7420656e6f756768206f66666572696e6720746f6b656e730000000000606082015260800190565b60208082526028908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20546040820152676f6f206561726c7960c01b606082015260800190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b6020808252602a908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a2049464f2068604082015269185cc81cdd185c9d195960b21b606082015260800190565b60208082526039908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a205060408201527f726f787920636f6e7472616374206e6f7420616c6c6f77656400000000000000606082015260800190565b6020808252603a908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a2049464f206861732073746172746564000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252603b908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a20456e64426c6f636b20746f6f206661720000000000606082015260800190565b60208082526054908201527f49464f496e697469616c697a61626c653a3a757064617465486172766573744260408201527f6c6f636b733a204e65772068617276657374426c6f636b206d75737420626520606082015273686967686572207468616e20656e64426c6f636b60601b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204160408201526a6c726561647920646f6e6560a81b606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260289082015260008051602061329b8339815191526040820152676f6f206561726c7960c01b606082015260800190565b60208082526059908201526000805160206132bb83398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f65206c6f776572207468616e206e657720656e64426c6f636b00000000000000608082015260a00190565b60208082526036908201527f49464f496e697469616c697a61626c653a3a75706461746548617276657374426040820152751b1bd8dadcce88125193c81a185cc81cdd185c9d195960521b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201527f657720616d6f756e742061626f76652075736572206c696d6974000000000000606082015260800190565b8151815260208083015190820152604080830151908201526060808301511515908201526080808301519082015260a0808301519082015260c0808301516001600160a01b039081169183019190915260e09283015116918101919091526101000190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b928352602083019190915260ff16604082015260600190565b60ff91909116815260200190565b60005b8381101561327b578181015183820152602001613263565b83811115611e155750506000910152565b8015158114611a3357600080fdfe49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205449464f496e697469616c697a61626c653a3a7570646174655374617274416e64a26469706673582212201d88ad7a7d7b30bf04f652ff2f487b0b02787e400af603a73122fa886a90fafd64736f6c634300060c0033