Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- IFODeployer
- Optimization enabled
- true
- Compiler version
- v0.6.12+commit.27d51765
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2022-04-28T10:11:12.342168Z
Constructor Arguments
000000000000000000000000b4c9817c173f20c342b05e91300ebffe4e680e42
Arg [0] (address) : 0xb4c9817c173f20c342b05e91300ebffe4e680e42
Contract source code
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; pragma experimental ABIEncoderV2; /* * @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 pragma solidity >=0.6.2 <0.8.0; /** * @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 pragma solidity =0.6.12; 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 pragma solidity =0.6.12; 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 pragma solidity 0.6.12; 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 pragma solidity >=0.6.2 <0.8.0; /** * @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 pragma solidity =0.6.12; /** @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 pragma solidity =0.6.12; 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); // 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); } } // File: contracts/IFODeployer.sol pragma solidity =0.6.12; /** * @title IFODeployer */ contract IFODeployer is Ownable { using SafeERC20 for IKRC20; uint256 public constant MAX_BUFFER_BLOCKS = 5184000; address public immutable mojitoProfile; event AdminTokenRecovery(address indexed tokenRecovered, uint256 amount); event NewIFOContract(address indexed ifoAddress); /** * @notice Constructor * @param _mojitoProfile: the address of the MojitoProfile */ constructor(address _mojitoProfile) public { mojitoProfile = _mojitoProfile; } /** * @notice It creates the IFO contract and initializes the contract. * @param _lpToken: the LP token used * @param _offeringToken: the token that is offered for the IFO * @param _startBlock: the start block for the IFO * @param _endBlock: the end block for the IFO * @param _adminAddress: the admin address for handling tokens */ function createIFO( address _lpToken, address _offeringToken, uint256 _startBlock, uint256 _endBlock, address _adminAddress ) external onlyOwner { require(IKRC20(_lpToken).totalSupply() >= 0); require(IKRC20(_offeringToken).totalSupply() >= 0); require(_lpToken != _offeringToken, "IFODeployer::createIFO: Tokens must be different"); require(_endBlock < (block.number + MAX_BUFFER_BLOCKS), "IFODeployer::createIFO: EndBlock too far"); require(_startBlock < _endBlock, "IFODeployer::createIFO: StartBlock must be inferior to endBlock"); require(_startBlock > block.number, "IFODeployer::createIFO: StartBlock must be greater than current block"); bytes memory bytecode = type(IFOInitializable).creationCode; bytes32 salt = keccak256(abi.encodePacked(_lpToken, _offeringToken, _startBlock)); address ifoAddress; assembly { ifoAddress := create2(0, add(bytecode, 32), mload(bytecode), salt) } IFOInitializable(ifoAddress).initialize( _lpToken, _offeringToken, mojitoProfile, _startBlock, _endBlock, MAX_BUFFER_BLOCKS, _adminAddress ); emit NewIFOContract(ifoAddress); } /** * @notice It allows the admin to recover wrong tokens sent to the contract * @param _tokenAddress: the address of the token to withdraw * @dev This function is only callable by admin. */ function recoverWrongTokens(address _tokenAddress) external onlyOwner { uint256 balanceToRecover = IKRC20(_tokenAddress).balanceOf(address(this)); require(balanceToRecover > 0, "IFODeployer::recoverWrongTokens: Balance must be > 0"); IKRC20(_tokenAddress).safeTransfer(address(msg.sender), balanceToRecover); emit AdminTokenRecovery(_tokenAddress, balanceToRecover); } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_mojitoProfile","internalType":"address"}]},{"type":"event","name":"AdminTokenRecovery","inputs":[{"type":"address","name":"tokenRecovered","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewIFOContract","inputs":[{"type":"address","name":"ifoAddress","internalType":"address","indexed":true}],"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":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_BUFFER_BLOCKS","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createIFO","inputs":[{"type":"address","name":"_lpToken","internalType":"address"},{"type":"address","name":"_offeringToken","internalType":"address"},{"type":"uint256","name":"_startBlock","internalType":"uint256"},{"type":"uint256","name":"_endBlock","internalType":"uint256"},{"type":"address","name":"_adminAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"mojitoProfile","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":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
Contract Creation Code
0x60a060405234801561001057600080fd5b5060405161428c38038061428c83398101604081905261002f9161009c565b6000610039610098565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35060601b6001600160601b0319166080526100ca565b3390565b6000602082840312156100ad578081fd5b81516001600160a01b03811681146100c3578182fd5b9392505050565b60805160601c6141a06100ec60003980610102528061054c52506141a06000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806385fc9d181161005b57806385fc9d18146100bd5780638da5cb5b146100d0578063915dfc31146100d8578063f2fde38b146100ed5761007d565b806306acd65f14610082578063715018a6146100a0578063746268cc146100aa575b600080fd5b61008a610100565b60405161009791906109e0565b60405180910390f35b6100a8610124565b005b6100a86100b83660046108d9565b6101b6565b6100a86100cb366004610900565b6102ef565b61008a6105ef565b6100e06105fe565b6040516100979190610d73565b6100a86100fb3660046108d9565b610605565b7f000000000000000000000000000000000000000000000000000000000000000081565b61012c6106c5565b6001600160a01b031661013d6105ef565b6001600160a01b03161461016c5760405162461bcd60e51b815260040161016390610c69565b60405180910390fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6101be6106c5565b6001600160a01b03166101cf6105ef565b6001600160a01b0316146101f55760405162461bcd60e51b815260040161016390610c69565b6040516370a0823160e01b81526000906001600160a01b038316906370a08231906102249030906004016109e0565b60206040518083038186803b15801561023c57600080fd5b505afa158015610250573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610274919061097e565b9050600081116102965760405162461bcd60e51b815260040161016390610d1f565b6102aa6001600160a01b03831633836106c9565b816001600160a01b03167f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129826040516102e39190610d73565b60405180910390a25050565b6102f76106c5565b6001600160a01b03166103086105ef565b6001600160a01b03161461032e5760405162461bcd60e51b815260040161016390610c69565b6000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561036957600080fd5b505afa15801561037d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a1919061097e565b10156103ac57600080fd5b6000846001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103e757600080fd5b505afa1580156103fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041f919061097e565b101561042a57600080fd5b836001600160a01b0316856001600160a01b0316141561045c5760405162461bcd60e51b815260040161016390610a7d565b624f1a00430182106104805760405162461bcd60e51b815260040161016390610b13565b81831061049f5760405162461bcd60e51b815260040161016390610c0c565b4383116104be5760405162461bcd60e51b815260040161016390610ba1565b6060604051806020016104d0906108cc565b6020820181038252601f19601f82011660405250905060008686866040516020016104fd93929190610996565b6040516020818303038152906040528051906020012090506000818351602085016000f56040516343aa5de360e11b81529091506001600160a01b03821690638754bbc69061057f908b908b907f0000000000000000000000000000000000000000000000000000000000000000908c908c90624f1a00908d906004016109f4565b600060405180830381600087803b15801561059957600080fd5b505af11580156105ad573d6000803e3d6000fd5b50506040516001600160a01b03841692507f3b823f91f23e8f12a60f36282813ffed043efcb2101731734672a4726c0864549150600090a25050505050505050565b6000546001600160a01b031690565b624f1a0081565b61060d6106c5565b6001600160a01b031661061e6105ef565b6001600160a01b0316146106445760405162461bcd60e51b815260040161016390610c69565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161016390610acd565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b61071f8363a9059cbb60e01b84846040516024016106e8929190610a31565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610724565b505050565b6060610779826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166107b39092919063ffffffff16565b80519091501561071f5780806020019051810190610797919061095e565b61071f5760405162461bcd60e51b815260040161016390610cd5565b60606107c284846000856107cc565b90505b9392505050565b6060824710156107ee5760405162461bcd60e51b815260040161016390610b5b565b6107f78561088d565b6108135760405162461bcd60e51b815260040161016390610c9e565b60006060866001600160a01b0316858760405161083091906109c4565b60006040518083038185875af1925050503d806000811461086d576040519150601f19603f3d011682016040523d82523d6000602084013e610872565b606091505b5091509150610882828286610893565b979650505050505050565b3b151590565b606083156108a25750816107c5565b8251156108b25782518084602001fd5b8160405162461bcd60e51b81526004016101639190610a4a565b6133a680610dc583390190565b6000602082840312156108ea578081fd5b81356001600160a01b03811681146107c5578182fd5b600080600080600060a08688031215610917578081fd5b853561092281610dac565b9450602086013561093281610dac565b93506040860135925060608601359150608086013561095081610dac565b809150509295509295909350565b60006020828403121561096f578081fd5b815180151581146107c5578182fd5b60006020828403121561098f578081fd5b5051919050565b6bffffffffffffffffffffffff19606094851b811682529290931b9091166014830152602882015260480190565b600082516109d6818460208701610d7c565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039788168152958716602087015293861660408601526060850192909252608084015260a083015290911660c082015260e00190565b6001600160a01b03929092168252602082015260400190565b6000602082528251806020840152610a69816040850160208701610d7c565b601f01601f19169190910160400192915050565b60208082526030908201527f49464f4465706c6f7965723a3a63726561746549464f3a20546f6b656e73206d60408201526f1d5cdd08189948191a5999995c995b9d60821b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526028908201527f49464f4465706c6f7965723a3a63726561746549464f3a20456e64426c6f636b604082015267103a37b7903330b960c11b606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b60208082526045908201527f49464f4465706c6f7965723a3a63726561746549464f3a205374617274426c6f60408201527f636b206d7573742062652067726561746572207468616e2063757272656e7420606082015264626c6f636b60d81b608082015260a00190565b6020808252603f908201527f49464f4465706c6f7965723a3a63726561746549464f3a205374617274426c6f60408201527f636b206d75737420626520696e666572696f7220746f20656e64426c6f636b00606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526034908201527f49464f4465706c6f7965723a3a7265636f76657257726f6e67546f6b656e733a60408201527302042616c616e6365206d757374206265203e20360641b606082015260800190565b90815260200190565b60005b83811015610d97578181015183820152602001610d7f565b83811115610da6576000848401525b50505050565b6001600160a01b0381168114610dc157600080fd5b5056fe60a060405234801561001057600080fd5b506001600090815561002061007a565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3503360601b60805261007e565b3390565b60805160601c613305620000a16000398061091b52806110d752506133056000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c8063760b31801161010f5780639f1b5248116100a2578063f076b0ad11610071578063f076b0ad146103ad578063f2fde38b146103b5578063f7b119a9146103c8578063f9cd5c12146103db576101e5565b80639f1b52481461036a578063a83e79751461037d578063b781360714610385578063ca463ca41461038d576101e5565b80638da5cb5b116100de5780638da5cb5b1461033f5780638ed5b0fc14610347578063915dfc311461034f5780639513997f14610357576101e5565b8063760b3180146102fe5780637edd5e34146103065780637f1bdd76146103195780638754bbc61461032c576101e5565b80633f138d4b1161018757806349681dad1161015657806349681dad146102c55780634af3c9b7146102cd5780635fcbd285146102ee578063715018a6146102f6576101e5565b80633f138d4b1461028257806345de0f771461029557806346ab91bf1461029d57806348cd4cb1146102bd576101e5565b806323f93574116101c357806323f93574146102325780632ee520d01461024757806337f859b81461025a578063392e53cd1461026d576101e5565b806306acd65f146101ea578063083c6323146102085780632374876c1461021d575b600080fd5b6101f26103fb565b6040516101ff919061242c565b60405180910390f35b61021061040a565b6040516101ff9190613201565b61023061022b3660046123bb565b610410565b005b61023a6106fa565b6040516101ff9190613247565b6102306102553660046122a4565b6106ff565b6102106102683660046122a4565b6107c0565b61027561081d565b6040516101ff9190612571565b61023061029036600461225e565b61082d565b6101f2610919565b6102b06102ab3660046122a4565b61093d565b6040516101ff919061319c565b6102106109cd565b6102106109d3565b6102e06102db3660046121dd565b6109d9565b6040516101ff929190612519565b6101f2610b1a565b610230610b29565b610210610bb2565b6102306103143660046122f5565b610bb8565b610230610327366004612387565b610c68565b61023061033a366004612169565b6110a2565b6101f2611180565b61021061118f565b610210611195565b6102306103653660046122d4565b61119b565b6102306103783660046122d4565b611298565b61021061147f565b6101f2611485565b6103a061039b3660046121dd565b611494565b6040516101ff919061249e565b6102106115bd565b6102306103c336600461214e565b6115c3565b6102306103d6366004612320565b611684565b6103ee6103e93660046121dd565b611890565b6040516101ff9190612506565b6005546001600160a01b031681565b60075481565b6002600054141561043c5760405162461bcd60e51b81526004016104339061300b565b60405180910390fd5b600260005561044a33611934565b156104675760405162461bcd60e51b815260040161043390612662565b3332146104865760405162461bcd60e51b815260040161043390612dd2565b60085443116104a75760405162461bcd60e51b815260040161043390612cf0565b600260ff8216106104ca5760405162461bcd60e51b815260040161043390612b9d565b336000908152601e6020908152604080832060ff851684529091529020546105045760405162461bcd60e51b815260040161043390612aa0565b336000908152601e6020908152604080832060ff808616855292529091206001015416156105445760405162461bcd60e51b815260040161043390612f76565b61054d3361193a565b336000818152601e6020908152604080832060ff8616845290915281206001908101805460ff1916909117905590819081906105899085611a36565b9194509250905080156105dc576105c081600d8660ff16600281106105aa57fe5b6008020160050154611c0b90919063ffffffff16565b600d8560ff16600281106105d057fe5b60080201600501819055505b821561068b576000600d8560ff16600281106105f457fe5b60080201600701546001600160a01b031690508061062857600454610623906001600160a01b03163386611c39565b610689565b604051637d0dabb560e01b81526001600160a01b03821690637d0dabb5906106569033908890600401612440565b600060405180830381600087803b15801561067057600080fd5b505af1158015610684573d6000803e3d6000fd5b505050505b505b81156106a8576003546106a8906001600160a01b03163384611c39565b8360ff16336001600160a01b03167f51524c2e5edfedf8b01b29719c661e4fbe27e71734e7cd773dabb7cb712fb3b385856040516106e792919061320a565b60405180910390a3505060016000555050565b600281565b610707611c94565b6001600160a01b0316610718611180565b6001600160a01b03161461073e5760405162461bcd60e51b815260040161043390612b68565b600654431061075f5760405162461bcd60e51b8152600401610433906130e9565b80600754106107805760405162461bcd60e51b815260040161043390612efc565b60088190556040517fb22cba2fc2aea10ed7f6b831e399a560a482f697c3d97de952482224da8bf845906107b5908390613201565b60405180910390a150565b6000600d82600281106107cf57fe5b600802016003015460ff166107e657506000610818565b610815600d83600281106107f657fe5b6008020160040154600d846002811061080b57fe5b6008020154611c98565b90505b919050565b600554600160a01b900460ff1681565b610835611c94565b6001600160a01b0316610846611180565b6001600160a01b03161461086c5760405162461bcd60e51b815260040161043390612b68565b6003546001600160a01b038381169116141561089a5760405162461bcd60e51b8152600401610433906128b1565b6004546001600160a01b03838116911614156108c85760405162461bcd60e51b815260040161043390612605565b6108dc6001600160a01b0383163383611c39565b7f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129828260405161090d929190612440565b60405180910390a15050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6109456120af565b600d826002811061095257fe5b6040805161010081018252600892909202929092018054825260018101546020830152600281015492820192909252600382015460ff161515606082015260048201546080820152600582015460a082015260068201546001600160a01b0390811660c083015260079092015490911660e082015292915050565b60065481565b600a5481565b606080808367ffffffffffffffff811180156109f457600080fd5b50604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50905060608467ffffffffffffffff81118015610a3a57600080fd5b50604051908082528060200260200182016040528015610a64578160200160208202803683370190505b50905060005b600260ff82161015610b0d576001600160a01b0388166000908152601e6020908152604080832060ff85168085529252909120548451909185918110610aac57fe5b6020908102919091018101919091526001600160a01b0389166000908152601e8252604080822060ff8086168085529190945291206001015484519216918491908110610af557fe5b91151560209283029190910190910152600101610a6a565b5090969095509350505050565b6003546001600160a01b031681565b610b31611c94565b6001600160a01b0316610b42611180565b6001600160a01b031614610b685760405162461bcd60e51b815260040161043390612b68565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600c5481565b610bc0611c94565b6001600160a01b0316610bd1611180565b6001600160a01b031614610bf75760405162461bcd60e51b815260040161043390612b68565b6007544310610c185760405162461bcd60e51b8152600401610433906125af565b600a8290556009839055600b8190556040517f2058a318dbdfd2edd92a32cfa0ee233a30b165b83b421830109cb22ae86f674590610c5b90859085908590613218565b60405180910390a1505050565b60026000541415610c8b5760405162461bcd60e51b81526004016104339061300b565b6002600055610c9933611934565b15610cb65760405162461bcd60e51b815260040161043390612662565b333214610cd55760405162461bcd60e51b815260040161043390612dd2565b60055460405163ea0d5dcd60e01b81526001600160a01b039091169063ea0d5dcd90610d0590339060040161242c565b60206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d559190612288565b610d715760405162461bcd60e51b8152600401610433906127d4565b600260ff821610610d945760405162461bcd60e51b815260040161043390612d38565b610d9e8133611d37565b610dba5760405162461bcd60e51b815260040161043390612732565b6000600d8260ff1660028110610dcc57fe5b6008020160010154118015610df557506000600d8260ff1660028110610dee57fe5b6008020154115b610e115760405162461bcd60e51b815260040161043390612789565b6006544311610e325760405162461bcd60e51b815260040161043390613042565b6007544310610e535760405162461bcd60e51b815260040161043390612af2565b60008211610e735760405162461bcd60e51b8152600401610433906129de565b6000600d8260ff1660028110610e8557fe5b60080201600701546001600160a01b0316905080610f3d57600c54600480546040516370a0823160e01b81526001600160a01b03909116916370a0823191610ecf9130910161242c565b60206040518083038186803b158015610ee757600080fd5b505afa158015610efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1f91906122bc565b1015610f3d5760405162461bcd60e51b815260040161043390612993565b600354610f55906001600160a01b0316333086611de9565b336000908152601e6020908152604080832060ff86168452909152902054610f7d9084611c0b565b336000908152601e6020908152604080832060ff8716808552925282209290925590600d9060028110610fac57fe5b6008020160020154111561100e57600d8260ff1660028110610fca57fe5b6008020160020154336000908152601e6020908152604080832060ff87168452909152902054111561100e5760405162461bcd60e51b81526004016104339061313f565b61103883600d8460ff166002811061102257fe5b6008020160040154611c0b90919063ffffffff16565b600d8360ff166002811061104857fe5b60080201600401819055508160ff16336001600160a01b03167ff763e680fce25a97ffd55d8b705370c98b47b2285f7b3b2900c43606fd418045856040516110909190613201565b60405180910390a35050600160005550565b600554600160a01b900460ff16156110cc5760405162461bcd60e51b815260040161043390612c42565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111145760405162461bcd60e51b815260040161043390612831565b60058054600380546001600160a01b03808c166001600160a01b031992831617909255600480548b841690831617905590881660ff60a01b19909216600160a01b17161790556006849055600783905560088390556002829055611177816115c3565b50505050505050565b6001546001600160a01b031690565b60095481565b60025481565b6111a3611c94565b6001600160a01b03166111b4611180565b6001600160a01b0316146111da5760405162461bcd60e51b815260040161043390612b68565b600254430181106111fd5760405162461bcd60e51b815260040161043390612eb1565b600654431061121e5760405162461bcd60e51b815260040161043390612e2f565b80821061123d5760405162461bcd60e51b815260040161043390613078565b81431061125c5760405162461bcd60e51b815260040161043390612a2f565b600682905560078190556040517f7cd0ab87d19036f3dfadadb232c78aa4879dda3f0c994a9d637532410ee2ce069061090d908490849061320a565b6112a0611c94565b6001600160a01b03166112b1611180565b6001600160a01b0316146112d75760405162461bcd60e51b815260040161043390612b68565b6003546040516370a0823160e01b81526001600160a01b03909116906370a082319061130790309060040161242c565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135791906122bc565b8211156113765760405162461bcd60e51b815260040161043390612bed565b600480546040516370a0823160e01b81526001600160a01b03909116916370a08231916113a59130910161242c565b60206040518083038186803b1580156113bd57600080fd5b505afa1580156113d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f591906122bc565b8111156114145760405162461bcd60e51b815260040161043390612c93565b811561143157600354611431906001600160a01b03163384611c39565b801561144e5760045461144e906001600160a01b03163383611c39565b7f94ebb62a252249c867ecb758d386f50a95be7e8df9e1c52917c9cf494327dd7d828260405161090d92919061320a565b600b5481565b6004546001600160a01b031681565b6060808267ffffffffffffffff811180156114ae57600080fd5b506040519080825280602002602001820160405280156114e857816020015b6114d5612108565b8152602001906001900390816114cd5790505b50905060005b60ff81168411156115b257600080600080600d89898760ff1681811061151057fe5b905060200201602081019061152591906123bb565b60ff166002811061153257fe5b600802015411156115725761156a8989898760ff1681811061155057fe5b905060200201602081019061156591906123bb565b611a36565b919450925090505b604051806060016040528084815260200183815260200182815250858560ff168151811061159c57fe5b60209081029190910101525050506001016114ee565b5090505b9392505050565b60085481565b6115cb611c94565b6001600160a01b03166115dc611180565b6001600160a01b0316146116025760405162461bcd60e51b815260040161043390612b68565b6001600160a01b0381166116285760405162461bcd60e51b8152600401610433906126b5565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b61168c611c94565b6001600160a01b031661169d611180565b6001600160a01b0316146116c35760405162461bcd60e51b815260040161043390612b68565b60065443106116e45760405162461bcd60e51b815260040161043390612d88565b600260ff8416106117075760405162461bcd60e51b815260040161043390612945565b86600d8460ff166002811061171857fe5b600802016001018190555085600d8460ff166002811061173457fe5b600802015584600d60ff85166002811061174a57fe5b600802016002018190555083600d8460ff166002811061176657fe5b6008020160030160006101000a81548160ff02191690831515021790555081600d8460ff166002811061179557fe5b6008020160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080600d8460ff16600281106117d157fe5b6008020160070160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600080600090505b600260ff821610156118445761183a600d8260ff166002811061182357fe5b600802016001015483611c0b90919063ffffffff16565b9150600101611804565b50600c8190556040517fddaf243a142670be60c19ff7116b5d8b124717b29bb4cc03cead42161614105b9061187e908a908a90889061322e565b60405180910390a15050505050505050565b6060808267ffffffffffffffff811180156118aa57600080fd5b506040519080825280602002602001820160405280156118d4578160200160208202803683370190505b50905060005b60ff81168411156115b2576119128686868460ff168181106118f857fe5b905060200201602081019061190d91906123bb565b611e10565b828260ff168151811061192157fe5b60209081029190910101526001016118da565b3b151590565b6001600160a01b0381166000908152601d602052604090205460ff16611a33576000805b600260ff8216101561199d57336000908152601e6020908152604080832060ff85168452909152902054611993908390611c0b565b915060010161195e565b50600b54811115611a31576001600160a01b038083166000908152601d602052604090819020805460ff19166001179055600554600a546009549251630dee0bfb60e11b81529190931692631bdc17f6926119fe9233929190600401612459565b600060405180830381600087803b158015611a1857600080fd5b505af1158015611a2c573d6000803e3d6000fd5b505050505b505b50565b600080600080600080600d8760ff1660028110611a4f57fe5b6008020154600d60ff891660028110611a6457fe5b60080201600401541115611b94576000611a7e8989611e10565b9050611ab964e8d4a51000611ab383600d8c60ff1660028110611a9d57fe5b6008020160010154611ea990919063ffffffff16565b90611ee3565b93506000611ae564e8d4a51000611ab384600d8d60ff1660028110611ada57fe5b600802015490611ea9565b6001600160a01b038b166000908152601e6020908152604080832060ff8e168452909152902054909150611b199082611f15565b9350600d8960ff1660028110611b2b57fe5b600802016003015460ff1615611b8d576000611b68600d8b60ff1660028110611b5057fe5b6008020160040154600d8c60ff166002811061080b57fe5b9050611b7d64e8d4a51000611ab38784611ea9565b9350611b898585611f15565b9450505b5050611bfd565b506000905080611bfa600d60ff891660028110611bad57fe5b6008020154611ab3600d60ff8b1660028110611bc557fe5b60080201600101546001600160a01b038c166000908152601e6020908152604080832060ff8f16845290915290205490611ea9565b92505b919450925090509250925092565b600082820183811015611c305760405162461bcd60e51b8152600401610433906126fb565b90505b92915050565b611c8f8363a9059cbb60e01b8484604051602401611c58929190612440565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f3d565b505050565b3390565b600080611ca58484611ee3565b90506105dc8110611cbd57631dcd6500915050611c33565b6103e88110611cd357633b9aca00915050611c33565b6101f48110611ce9576377359400915050611c33565b60fa8110611cfe57639502f900915050611c33565b60648110611d135763b2d05e00915050611c33565b60328110611d295764012a05f200915050611c33565b6402540be400915050611c33565b600080600d8460ff1660028110611d4a57fe5b60080201600601546001600160a01b03169050801580611de15750604051633af32abf60e01b81526001600160a01b03821690633af32abf90611d9190869060040161242c565b60206040518083038186803b158015611da957600080fd5b505afa158015611dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de19190612288565b949350505050565b611e0a846323b872dd60e01b858585604051602401611c589392919061247a565b50505050565b600080600d8360ff1660028110611e2357fe5b60080201600401541115611ea157611e9a611e61620f4240600d8560ff1660028110611e4b57fe5b6008020160040154611ea990919063ffffffff16565b6001600160a01b0385166000908152601e6020908152604080832060ff88168452909152902054611ab390670de0b6b3a7640000611ea9565b9050611c33565b506000611c33565b600082611eb857506000611c33565b82820282848281611ec557fe5b0414611c305760405162461bcd60e51b815260040161043390612b27565b6000808211611f045760405162461bcd60e51b81526004016104339061290e565b818381611f0d57fe5b049392505050565b600082821115611f375760405162461bcd60e51b81526004016104339061287a565b50900390565b6060611f92826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fcc9092919063ffffffff16565b805190915015611c8f5780806020019051810190611fb09190612288565b611c8f5760405162461bcd60e51b815260040161043390612fc1565b6060611de1848460008585611fe085611934565b611ffc5760405162461bcd60e51b815260040161043390612e7a565b60006060866001600160a01b031685876040516120199190612410565b60006040518083038185875af1925050503d8060008114612056576040519150601f19603f3d011682016040523d82523d6000602084013e61205b565b606091505b509150915061206b828286612076565b979650505050505050565b606083156120855750816115b6565b8251156120955782518084602001fd5b8160405162461bcd60e51b8152600401610433919061257c565b604051806101000160405280600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b60405180606001604052806003906020820280368337509192915050565b80356001600160a01b0381168114611c3357600080fd5b803560ff81168114611c3357600080fd5b60006020828403121561215f578081fd5b6115b68383612126565b600080600080600080600060e0888a031215612183578283fd5b61218d8989612126565b965061219c8960208a01612126565b95506121ab8960408a01612126565b9450606088013593506080880135925060a088013591506121cf8960c08a01612126565b905092959891949750929550565b6000806000604084860312156121f1578283fd5b6121fb8585612126565b9250602084013567ffffffffffffffff80821115612217578384fd5b818601915086601f83011261222a578384fd5b813581811115612238578485fd5b876020808302850101111561224b578485fd5b6020830194508093505050509250925092565b60008060408385031215612270578182fd5b61227a8484612126565b946020939093013593505050565b600060208284031215612299578081fd5b8151611c3081613281565b6000602082840312156122b5578081fd5b5035919050565b6000602082840312156122cd578081fd5b5051919050565b600080604083850312156122e6578182fd5b50508035926020909101359150565b600080600060608486031215612309578283fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a03121561233a578283fd5b873596506020880135955060408801359450606088013561235a81613281565b93506123698960808a0161213d565b92506123788960a08a01612126565b91506121cf8960c08a01612126565b60008060408385031215612399578182fd5b82359150602083013560ff811681146123b0578182fd5b809150509250929050565b6000602082840312156123cc578081fd5b6115b6838361213d565b6000815180845260208085019450808401835b83811015612405578151875295820195908201906001016123e9565b509495945050505050565b60008251612422818460208701613255565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252825182820181905260009190848201906040850190845b818110156124fa57835183875b60038110156124e4578251825291870191908701906001016124c7565b50505092840192606092909201916001016124ba565b50909695505050505050565b6000602082526115b660208301846123d6565b60006040825261252c60408301856123d6565b828103602084810191909152845180835285820192820190845b81811015612564578451151583529383019391830191600101612546565b5090979650505050505050565b901515815260200190565b600060208252825180602084015261259b816040850160208701613255565b601f01601f19169190910160400192915050565b60208082526036908201527f49464f496e697469616c697a61626c653a3a757064617465506f696e74506172604082015275185b595d195c9cce88125193c81a185cc8195b99195960521b606082015260800190565b6020808252603e908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265206f66666572696e6720746f6b656e0000606082015260800190565b60208082526033908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a20436040820152721bdb9d1c9858dd081b9bdd08185b1b1bddd959606a1b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260439082015260008051602061329083398151915260408201527f6869732061646472657373206973206e6f7420696e207468652077686974656c6060820152621a5cdd60ea1b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205060408201526a1bdbdb081b9bdd081cd95d60aa1b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204d60408201527f757374206861766520616e206163746976652070726f66696c65000000000000606082015260800190565b60208082526029908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a204e6f6040820152687420666163746f727960b81b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526038908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265204c5020746f6b656e0000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252602e908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a20506f6f6c2060408201526d191bd95cc81b9bdd08195e1a5cdd60921b606082015260800190565b6020808252603c9082015260008051602061329083398151915260408201527f6f6b656e73206e6f74206465706f73697465642070726f7065726c7900000000606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204160408201527006d6f756e74206d757374206265203e203607c1b606082015260800190565b6020808252605b908201526000805160206132b083398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f6520686967686572207468616e2063757272656e7420626c6f636b0000000000608082015260a00190565b60208082526032908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20446040820152716964206e6f7420706172746963697061746560701b606082015260800190565b60208082526027908201526000805160206132908339815191526040820152666f6f206c61746560c81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b60208082526035908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a604082015274204e6f7420656e6f756768204c5020746f6b656e7360581b606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a20416c6040820152701c9958591e481a5b9a5d1a585b1a5e9959607a1b606082015260800190565b6020808252603b908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a60408201527f204e6f7420656e6f756768206f66666572696e6720746f6b656e730000000000606082015260800190565b60208082526028908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20546040820152676f6f206561726c7960c01b606082015260800190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b6020808252602a908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a2049464f2068604082015269185cc81cdd185c9d195960b21b606082015260800190565b60208082526039908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a205060408201527f726f787920636f6e7472616374206e6f7420616c6c6f77656400000000000000606082015260800190565b6020808252603a908201526000805160206132b083398151915260408201527f456e64426c6f636b733a2049464f206861732073746172746564000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252603b908201526000805160206132b083398151915260408201527f456e64426c6f636b733a20456e64426c6f636b20746f6f206661720000000000606082015260800190565b60208082526054908201527f49464f496e697469616c697a61626c653a3a757064617465486172766573744260408201527f6c6f636b733a204e65772068617276657374426c6f636b206d75737420626520606082015273686967686572207468616e20656e64426c6f636b60601b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204160408201526a6c726561647920646f6e6560a81b606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526028908201526000805160206132908339815191526040820152676f6f206561726c7960c01b606082015260800190565b60208082526059908201526000805160206132b083398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f65206c6f776572207468616e206e657720656e64426c6f636b00000000000000608082015260a00190565b60208082526036908201527f49464f496e697469616c697a61626c653a3a75706461746548617276657374426040820152751b1bd8dadcce88125193c81a185cc81cdd185c9d195960521b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201527f657720616d6f756e742061626f76652075736572206c696d6974000000000000606082015260800190565b8151815260208083015190820152604080830151908201526060808301511515908201526080808301519082015260a0808301519082015260c0808301516001600160a01b039081169183019190915260e09283015116918101919091526101000190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b928352602083019190915260ff16604082015260600190565b60ff91909116815260200190565b60005b83811015613270578181015183820152602001613258565b83811115611e0a5750506000910152565b8015158114611a3357600080fdfe49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205449464f496e697469616c697a61626c653a3a7570646174655374617274416e64a26469706673582212200fc44f4c45ad0b5a0349ada0a1ded444fbce43eeb4d1d3c4e2111ede6b269d2364736f6c634300060c0033a264697066735822122049f77587d12698f94d277b2044a2c3c170de59abc9e4c1cb19875e0ef6aa487064736f6c634300060c0033000000000000000000000000b4c9817c173f20c342b05e91300ebffe4e680e42
Deployed ByteCode
0x608060405234801561001057600080fd5b506004361061007d5760003560e01c806385fc9d181161005b57806385fc9d18146100bd5780638da5cb5b146100d0578063915dfc31146100d8578063f2fde38b146100ed5761007d565b806306acd65f14610082578063715018a6146100a0578063746268cc146100aa575b600080fd5b61008a610100565b60405161009791906109e0565b60405180910390f35b6100a8610124565b005b6100a86100b83660046108d9565b6101b6565b6100a86100cb366004610900565b6102ef565b61008a6105ef565b6100e06105fe565b6040516100979190610d73565b6100a86100fb3660046108d9565b610605565b7f000000000000000000000000b4c9817c173f20c342b05e91300ebffe4e680e4281565b61012c6106c5565b6001600160a01b031661013d6105ef565b6001600160a01b03161461016c5760405162461bcd60e51b815260040161016390610c69565b60405180910390fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6101be6106c5565b6001600160a01b03166101cf6105ef565b6001600160a01b0316146101f55760405162461bcd60e51b815260040161016390610c69565b6040516370a0823160e01b81526000906001600160a01b038316906370a08231906102249030906004016109e0565b60206040518083038186803b15801561023c57600080fd5b505afa158015610250573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610274919061097e565b9050600081116102965760405162461bcd60e51b815260040161016390610d1f565b6102aa6001600160a01b03831633836106c9565b816001600160a01b03167f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129826040516102e39190610d73565b60405180910390a25050565b6102f76106c5565b6001600160a01b03166103086105ef565b6001600160a01b03161461032e5760405162461bcd60e51b815260040161016390610c69565b6000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561036957600080fd5b505afa15801561037d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a1919061097e565b10156103ac57600080fd5b6000846001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103e757600080fd5b505afa1580156103fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041f919061097e565b101561042a57600080fd5b836001600160a01b0316856001600160a01b0316141561045c5760405162461bcd60e51b815260040161016390610a7d565b624f1a00430182106104805760405162461bcd60e51b815260040161016390610b13565b81831061049f5760405162461bcd60e51b815260040161016390610c0c565b4383116104be5760405162461bcd60e51b815260040161016390610ba1565b6060604051806020016104d0906108cc565b6020820181038252601f19601f82011660405250905060008686866040516020016104fd93929190610996565b6040516020818303038152906040528051906020012090506000818351602085016000f56040516343aa5de360e11b81529091506001600160a01b03821690638754bbc69061057f908b908b907f000000000000000000000000b4c9817c173f20c342b05e91300ebffe4e680e42908c908c90624f1a00908d906004016109f4565b600060405180830381600087803b15801561059957600080fd5b505af11580156105ad573d6000803e3d6000fd5b50506040516001600160a01b03841692507f3b823f91f23e8f12a60f36282813ffed043efcb2101731734672a4726c0864549150600090a25050505050505050565b6000546001600160a01b031690565b624f1a0081565b61060d6106c5565b6001600160a01b031661061e6105ef565b6001600160a01b0316146106445760405162461bcd60e51b815260040161016390610c69565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161016390610acd565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b61071f8363a9059cbb60e01b84846040516024016106e8929190610a31565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610724565b505050565b6060610779826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166107b39092919063ffffffff16565b80519091501561071f5780806020019051810190610797919061095e565b61071f5760405162461bcd60e51b815260040161016390610cd5565b60606107c284846000856107cc565b90505b9392505050565b6060824710156107ee5760405162461bcd60e51b815260040161016390610b5b565b6107f78561088d565b6108135760405162461bcd60e51b815260040161016390610c9e565b60006060866001600160a01b0316858760405161083091906109c4565b60006040518083038185875af1925050503d806000811461086d576040519150601f19603f3d011682016040523d82523d6000602084013e610872565b606091505b5091509150610882828286610893565b979650505050505050565b3b151590565b606083156108a25750816107c5565b8251156108b25782518084602001fd5b8160405162461bcd60e51b81526004016101639190610a4a565b6133a680610dc583390190565b6000602082840312156108ea578081fd5b81356001600160a01b03811681146107c5578182fd5b600080600080600060a08688031215610917578081fd5b853561092281610dac565b9450602086013561093281610dac565b93506040860135925060608601359150608086013561095081610dac565b809150509295509295909350565b60006020828403121561096f578081fd5b815180151581146107c5578182fd5b60006020828403121561098f578081fd5b5051919050565b6bffffffffffffffffffffffff19606094851b811682529290931b9091166014830152602882015260480190565b600082516109d6818460208701610d7c565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039788168152958716602087015293861660408601526060850192909252608084015260a083015290911660c082015260e00190565b6001600160a01b03929092168252602082015260400190565b6000602082528251806020840152610a69816040850160208701610d7c565b601f01601f19169190910160400192915050565b60208082526030908201527f49464f4465706c6f7965723a3a63726561746549464f3a20546f6b656e73206d60408201526f1d5cdd08189948191a5999995c995b9d60821b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526028908201527f49464f4465706c6f7965723a3a63726561746549464f3a20456e64426c6f636b604082015267103a37b7903330b960c11b606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b60208082526045908201527f49464f4465706c6f7965723a3a63726561746549464f3a205374617274426c6f60408201527f636b206d7573742062652067726561746572207468616e2063757272656e7420606082015264626c6f636b60d81b608082015260a00190565b6020808252603f908201527f49464f4465706c6f7965723a3a63726561746549464f3a205374617274426c6f60408201527f636b206d75737420626520696e666572696f7220746f20656e64426c6f636b00606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526034908201527f49464f4465706c6f7965723a3a7265636f76657257726f6e67546f6b656e733a60408201527302042616c616e6365206d757374206265203e20360641b606082015260800190565b90815260200190565b60005b83811015610d97578181015183820152602001610d7f565b83811115610da6576000848401525b50505050565b6001600160a01b0381168114610dc157600080fd5b5056fe60a060405234801561001057600080fd5b506001600090815561002061007a565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3503360601b60805261007e565b3390565b60805160601c613305620000a16000398061091b52806110d752506133056000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c8063760b31801161010f5780639f1b5248116100a2578063f076b0ad11610071578063f076b0ad146103ad578063f2fde38b146103b5578063f7b119a9146103c8578063f9cd5c12146103db576101e5565b80639f1b52481461036a578063a83e79751461037d578063b781360714610385578063ca463ca41461038d576101e5565b80638da5cb5b116100de5780638da5cb5b1461033f5780638ed5b0fc14610347578063915dfc311461034f5780639513997f14610357576101e5565b8063760b3180146102fe5780637edd5e34146103065780637f1bdd76146103195780638754bbc61461032c576101e5565b80633f138d4b1161018757806349681dad1161015657806349681dad146102c55780634af3c9b7146102cd5780635fcbd285146102ee578063715018a6146102f6576101e5565b80633f138d4b1461028257806345de0f771461029557806346ab91bf1461029d57806348cd4cb1146102bd576101e5565b806323f93574116101c357806323f93574146102325780632ee520d01461024757806337f859b81461025a578063392e53cd1461026d576101e5565b806306acd65f146101ea578063083c6323146102085780632374876c1461021d575b600080fd5b6101f26103fb565b6040516101ff919061242c565b60405180910390f35b61021061040a565b6040516101ff9190613201565b61023061022b3660046123bb565b610410565b005b61023a6106fa565b6040516101ff9190613247565b6102306102553660046122a4565b6106ff565b6102106102683660046122a4565b6107c0565b61027561081d565b6040516101ff9190612571565b61023061029036600461225e565b61082d565b6101f2610919565b6102b06102ab3660046122a4565b61093d565b6040516101ff919061319c565b6102106109cd565b6102106109d3565b6102e06102db3660046121dd565b6109d9565b6040516101ff929190612519565b6101f2610b1a565b610230610b29565b610210610bb2565b6102306103143660046122f5565b610bb8565b610230610327366004612387565b610c68565b61023061033a366004612169565b6110a2565b6101f2611180565b61021061118f565b610210611195565b6102306103653660046122d4565b61119b565b6102306103783660046122d4565b611298565b61021061147f565b6101f2611485565b6103a061039b3660046121dd565b611494565b6040516101ff919061249e565b6102106115bd565b6102306103c336600461214e565b6115c3565b6102306103d6366004612320565b611684565b6103ee6103e93660046121dd565b611890565b6040516101ff9190612506565b6005546001600160a01b031681565b60075481565b6002600054141561043c5760405162461bcd60e51b81526004016104339061300b565b60405180910390fd5b600260005561044a33611934565b156104675760405162461bcd60e51b815260040161043390612662565b3332146104865760405162461bcd60e51b815260040161043390612dd2565b60085443116104a75760405162461bcd60e51b815260040161043390612cf0565b600260ff8216106104ca5760405162461bcd60e51b815260040161043390612b9d565b336000908152601e6020908152604080832060ff851684529091529020546105045760405162461bcd60e51b815260040161043390612aa0565b336000908152601e6020908152604080832060ff808616855292529091206001015416156105445760405162461bcd60e51b815260040161043390612f76565b61054d3361193a565b336000818152601e6020908152604080832060ff8616845290915281206001908101805460ff1916909117905590819081906105899085611a36565b9194509250905080156105dc576105c081600d8660ff16600281106105aa57fe5b6008020160050154611c0b90919063ffffffff16565b600d8560ff16600281106105d057fe5b60080201600501819055505b821561068b576000600d8560ff16600281106105f457fe5b60080201600701546001600160a01b031690508061062857600454610623906001600160a01b03163386611c39565b610689565b604051637d0dabb560e01b81526001600160a01b03821690637d0dabb5906106569033908890600401612440565b600060405180830381600087803b15801561067057600080fd5b505af1158015610684573d6000803e3d6000fd5b505050505b505b81156106a8576003546106a8906001600160a01b03163384611c39565b8360ff16336001600160a01b03167f51524c2e5edfedf8b01b29719c661e4fbe27e71734e7cd773dabb7cb712fb3b385856040516106e792919061320a565b60405180910390a3505060016000555050565b600281565b610707611c94565b6001600160a01b0316610718611180565b6001600160a01b03161461073e5760405162461bcd60e51b815260040161043390612b68565b600654431061075f5760405162461bcd60e51b8152600401610433906130e9565b80600754106107805760405162461bcd60e51b815260040161043390612efc565b60088190556040517fb22cba2fc2aea10ed7f6b831e399a560a482f697c3d97de952482224da8bf845906107b5908390613201565b60405180910390a150565b6000600d82600281106107cf57fe5b600802016003015460ff166107e657506000610818565b610815600d83600281106107f657fe5b6008020160040154600d846002811061080b57fe5b6008020154611c98565b90505b919050565b600554600160a01b900460ff1681565b610835611c94565b6001600160a01b0316610846611180565b6001600160a01b03161461086c5760405162461bcd60e51b815260040161043390612b68565b6003546001600160a01b038381169116141561089a5760405162461bcd60e51b8152600401610433906128b1565b6004546001600160a01b03838116911614156108c85760405162461bcd60e51b815260040161043390612605565b6108dc6001600160a01b0383163383611c39565b7f74545154aac348a3eac92596bd1971957ca94795f4e954ec5f613b55fab78129828260405161090d929190612440565b60405180910390a15050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6109456120af565b600d826002811061095257fe5b6040805161010081018252600892909202929092018054825260018101546020830152600281015492820192909252600382015460ff161515606082015260048201546080820152600582015460a082015260068201546001600160a01b0390811660c083015260079092015490911660e082015292915050565b60065481565b600a5481565b606080808367ffffffffffffffff811180156109f457600080fd5b50604051908082528060200260200182016040528015610a1e578160200160208202803683370190505b50905060608467ffffffffffffffff81118015610a3a57600080fd5b50604051908082528060200260200182016040528015610a64578160200160208202803683370190505b50905060005b600260ff82161015610b0d576001600160a01b0388166000908152601e6020908152604080832060ff85168085529252909120548451909185918110610aac57fe5b6020908102919091018101919091526001600160a01b0389166000908152601e8252604080822060ff8086168085529190945291206001015484519216918491908110610af557fe5b91151560209283029190910190910152600101610a6a565b5090969095509350505050565b6003546001600160a01b031681565b610b31611c94565b6001600160a01b0316610b42611180565b6001600160a01b031614610b685760405162461bcd60e51b815260040161043390612b68565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600c5481565b610bc0611c94565b6001600160a01b0316610bd1611180565b6001600160a01b031614610bf75760405162461bcd60e51b815260040161043390612b68565b6007544310610c185760405162461bcd60e51b8152600401610433906125af565b600a8290556009839055600b8190556040517f2058a318dbdfd2edd92a32cfa0ee233a30b165b83b421830109cb22ae86f674590610c5b90859085908590613218565b60405180910390a1505050565b60026000541415610c8b5760405162461bcd60e51b81526004016104339061300b565b6002600055610c9933611934565b15610cb65760405162461bcd60e51b815260040161043390612662565b333214610cd55760405162461bcd60e51b815260040161043390612dd2565b60055460405163ea0d5dcd60e01b81526001600160a01b039091169063ea0d5dcd90610d0590339060040161242c565b60206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d559190612288565b610d715760405162461bcd60e51b8152600401610433906127d4565b600260ff821610610d945760405162461bcd60e51b815260040161043390612d38565b610d9e8133611d37565b610dba5760405162461bcd60e51b815260040161043390612732565b6000600d8260ff1660028110610dcc57fe5b6008020160010154118015610df557506000600d8260ff1660028110610dee57fe5b6008020154115b610e115760405162461bcd60e51b815260040161043390612789565b6006544311610e325760405162461bcd60e51b815260040161043390613042565b6007544310610e535760405162461bcd60e51b815260040161043390612af2565b60008211610e735760405162461bcd60e51b8152600401610433906129de565b6000600d8260ff1660028110610e8557fe5b60080201600701546001600160a01b0316905080610f3d57600c54600480546040516370a0823160e01b81526001600160a01b03909116916370a0823191610ecf9130910161242c565b60206040518083038186803b158015610ee757600080fd5b505afa158015610efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1f91906122bc565b1015610f3d5760405162461bcd60e51b815260040161043390612993565b600354610f55906001600160a01b0316333086611de9565b336000908152601e6020908152604080832060ff86168452909152902054610f7d9084611c0b565b336000908152601e6020908152604080832060ff8716808552925282209290925590600d9060028110610fac57fe5b6008020160020154111561100e57600d8260ff1660028110610fca57fe5b6008020160020154336000908152601e6020908152604080832060ff87168452909152902054111561100e5760405162461bcd60e51b81526004016104339061313f565b61103883600d8460ff166002811061102257fe5b6008020160040154611c0b90919063ffffffff16565b600d8360ff166002811061104857fe5b60080201600401819055508160ff16336001600160a01b03167ff763e680fce25a97ffd55d8b705370c98b47b2285f7b3b2900c43606fd418045856040516110909190613201565b60405180910390a35050600160005550565b600554600160a01b900460ff16156110cc5760405162461bcd60e51b815260040161043390612c42565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111145760405162461bcd60e51b815260040161043390612831565b60058054600380546001600160a01b03808c166001600160a01b031992831617909255600480548b841690831617905590881660ff60a01b19909216600160a01b17161790556006849055600783905560088390556002829055611177816115c3565b50505050505050565b6001546001600160a01b031690565b60095481565b60025481565b6111a3611c94565b6001600160a01b03166111b4611180565b6001600160a01b0316146111da5760405162461bcd60e51b815260040161043390612b68565b600254430181106111fd5760405162461bcd60e51b815260040161043390612eb1565b600654431061121e5760405162461bcd60e51b815260040161043390612e2f565b80821061123d5760405162461bcd60e51b815260040161043390613078565b81431061125c5760405162461bcd60e51b815260040161043390612a2f565b600682905560078190556040517f7cd0ab87d19036f3dfadadb232c78aa4879dda3f0c994a9d637532410ee2ce069061090d908490849061320a565b6112a0611c94565b6001600160a01b03166112b1611180565b6001600160a01b0316146112d75760405162461bcd60e51b815260040161043390612b68565b6003546040516370a0823160e01b81526001600160a01b03909116906370a082319061130790309060040161242c565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135791906122bc565b8211156113765760405162461bcd60e51b815260040161043390612bed565b600480546040516370a0823160e01b81526001600160a01b03909116916370a08231916113a59130910161242c565b60206040518083038186803b1580156113bd57600080fd5b505afa1580156113d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f591906122bc565b8111156114145760405162461bcd60e51b815260040161043390612c93565b811561143157600354611431906001600160a01b03163384611c39565b801561144e5760045461144e906001600160a01b03163383611c39565b7f94ebb62a252249c867ecb758d386f50a95be7e8df9e1c52917c9cf494327dd7d828260405161090d92919061320a565b600b5481565b6004546001600160a01b031681565b6060808267ffffffffffffffff811180156114ae57600080fd5b506040519080825280602002602001820160405280156114e857816020015b6114d5612108565b8152602001906001900390816114cd5790505b50905060005b60ff81168411156115b257600080600080600d89898760ff1681811061151057fe5b905060200201602081019061152591906123bb565b60ff166002811061153257fe5b600802015411156115725761156a8989898760ff1681811061155057fe5b905060200201602081019061156591906123bb565b611a36565b919450925090505b604051806060016040528084815260200183815260200182815250858560ff168151811061159c57fe5b60209081029190910101525050506001016114ee565b5090505b9392505050565b60085481565b6115cb611c94565b6001600160a01b03166115dc611180565b6001600160a01b0316146116025760405162461bcd60e51b815260040161043390612b68565b6001600160a01b0381166116285760405162461bcd60e51b8152600401610433906126b5565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b61168c611c94565b6001600160a01b031661169d611180565b6001600160a01b0316146116c35760405162461bcd60e51b815260040161043390612b68565b60065443106116e45760405162461bcd60e51b815260040161043390612d88565b600260ff8416106117075760405162461bcd60e51b815260040161043390612945565b86600d8460ff166002811061171857fe5b600802016001018190555085600d8460ff166002811061173457fe5b600802015584600d60ff85166002811061174a57fe5b600802016002018190555083600d8460ff166002811061176657fe5b6008020160030160006101000a81548160ff02191690831515021790555081600d8460ff166002811061179557fe5b6008020160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080600d8460ff16600281106117d157fe5b6008020160070160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600080600090505b600260ff821610156118445761183a600d8260ff166002811061182357fe5b600802016001015483611c0b90919063ffffffff16565b9150600101611804565b50600c8190556040517fddaf243a142670be60c19ff7116b5d8b124717b29bb4cc03cead42161614105b9061187e908a908a90889061322e565b60405180910390a15050505050505050565b6060808267ffffffffffffffff811180156118aa57600080fd5b506040519080825280602002602001820160405280156118d4578160200160208202803683370190505b50905060005b60ff81168411156115b2576119128686868460ff168181106118f857fe5b905060200201602081019061190d91906123bb565b611e10565b828260ff168151811061192157fe5b60209081029190910101526001016118da565b3b151590565b6001600160a01b0381166000908152601d602052604090205460ff16611a33576000805b600260ff8216101561199d57336000908152601e6020908152604080832060ff85168452909152902054611993908390611c0b565b915060010161195e565b50600b54811115611a31576001600160a01b038083166000908152601d602052604090819020805460ff19166001179055600554600a546009549251630dee0bfb60e11b81529190931692631bdc17f6926119fe9233929190600401612459565b600060405180830381600087803b158015611a1857600080fd5b505af1158015611a2c573d6000803e3d6000fd5b505050505b505b50565b600080600080600080600d8760ff1660028110611a4f57fe5b6008020154600d60ff891660028110611a6457fe5b60080201600401541115611b94576000611a7e8989611e10565b9050611ab964e8d4a51000611ab383600d8c60ff1660028110611a9d57fe5b6008020160010154611ea990919063ffffffff16565b90611ee3565b93506000611ae564e8d4a51000611ab384600d8d60ff1660028110611ada57fe5b600802015490611ea9565b6001600160a01b038b166000908152601e6020908152604080832060ff8e168452909152902054909150611b199082611f15565b9350600d8960ff1660028110611b2b57fe5b600802016003015460ff1615611b8d576000611b68600d8b60ff1660028110611b5057fe5b6008020160040154600d8c60ff166002811061080b57fe5b9050611b7d64e8d4a51000611ab38784611ea9565b9350611b898585611f15565b9450505b5050611bfd565b506000905080611bfa600d60ff891660028110611bad57fe5b6008020154611ab3600d60ff8b1660028110611bc557fe5b60080201600101546001600160a01b038c166000908152601e6020908152604080832060ff8f16845290915290205490611ea9565b92505b919450925090509250925092565b600082820183811015611c305760405162461bcd60e51b8152600401610433906126fb565b90505b92915050565b611c8f8363a9059cbb60e01b8484604051602401611c58929190612440565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f3d565b505050565b3390565b600080611ca58484611ee3565b90506105dc8110611cbd57631dcd6500915050611c33565b6103e88110611cd357633b9aca00915050611c33565b6101f48110611ce9576377359400915050611c33565b60fa8110611cfe57639502f900915050611c33565b60648110611d135763b2d05e00915050611c33565b60328110611d295764012a05f200915050611c33565b6402540be400915050611c33565b600080600d8460ff1660028110611d4a57fe5b60080201600601546001600160a01b03169050801580611de15750604051633af32abf60e01b81526001600160a01b03821690633af32abf90611d9190869060040161242c565b60206040518083038186803b158015611da957600080fd5b505afa158015611dbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de19190612288565b949350505050565b611e0a846323b872dd60e01b858585604051602401611c589392919061247a565b50505050565b600080600d8360ff1660028110611e2357fe5b60080201600401541115611ea157611e9a611e61620f4240600d8560ff1660028110611e4b57fe5b6008020160040154611ea990919063ffffffff16565b6001600160a01b0385166000908152601e6020908152604080832060ff88168452909152902054611ab390670de0b6b3a7640000611ea9565b9050611c33565b506000611c33565b600082611eb857506000611c33565b82820282848281611ec557fe5b0414611c305760405162461bcd60e51b815260040161043390612b27565b6000808211611f045760405162461bcd60e51b81526004016104339061290e565b818381611f0d57fe5b049392505050565b600082821115611f375760405162461bcd60e51b81526004016104339061287a565b50900390565b6060611f92826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fcc9092919063ffffffff16565b805190915015611c8f5780806020019051810190611fb09190612288565b611c8f5760405162461bcd60e51b815260040161043390612fc1565b6060611de1848460008585611fe085611934565b611ffc5760405162461bcd60e51b815260040161043390612e7a565b60006060866001600160a01b031685876040516120199190612410565b60006040518083038185875af1925050503d8060008114612056576040519150601f19603f3d011682016040523d82523d6000602084013e61205b565b606091505b509150915061206b828286612076565b979650505050505050565b606083156120855750816115b6565b8251156120955782518084602001fd5b8160405162461bcd60e51b8152600401610433919061257c565b604051806101000160405280600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b60405180606001604052806003906020820280368337509192915050565b80356001600160a01b0381168114611c3357600080fd5b803560ff81168114611c3357600080fd5b60006020828403121561215f578081fd5b6115b68383612126565b600080600080600080600060e0888a031215612183578283fd5b61218d8989612126565b965061219c8960208a01612126565b95506121ab8960408a01612126565b9450606088013593506080880135925060a088013591506121cf8960c08a01612126565b905092959891949750929550565b6000806000604084860312156121f1578283fd5b6121fb8585612126565b9250602084013567ffffffffffffffff80821115612217578384fd5b818601915086601f83011261222a578384fd5b813581811115612238578485fd5b876020808302850101111561224b578485fd5b6020830194508093505050509250925092565b60008060408385031215612270578182fd5b61227a8484612126565b946020939093013593505050565b600060208284031215612299578081fd5b8151611c3081613281565b6000602082840312156122b5578081fd5b5035919050565b6000602082840312156122cd578081fd5b5051919050565b600080604083850312156122e6578182fd5b50508035926020909101359150565b600080600060608486031215612309578283fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a03121561233a578283fd5b873596506020880135955060408801359450606088013561235a81613281565b93506123698960808a0161213d565b92506123788960a08a01612126565b91506121cf8960c08a01612126565b60008060408385031215612399578182fd5b82359150602083013560ff811681146123b0578182fd5b809150509250929050565b6000602082840312156123cc578081fd5b6115b6838361213d565b6000815180845260208085019450808401835b83811015612405578151875295820195908201906001016123e9565b509495945050505050565b60008251612422818460208701613255565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252825182820181905260009190848201906040850190845b818110156124fa57835183875b60038110156124e4578251825291870191908701906001016124c7565b50505092840192606092909201916001016124ba565b50909695505050505050565b6000602082526115b660208301846123d6565b60006040825261252c60408301856123d6565b828103602084810191909152845180835285820192820190845b81811015612564578451151583529383019391830191600101612546565b5090979650505050505050565b901515815260200190565b600060208252825180602084015261259b816040850160208701613255565b601f01601f19169190910160400192915050565b60208082526036908201527f49464f496e697469616c697a61626c653a3a757064617465506f696e74506172604082015275185b595d195c9cce88125193c81a185cc8195b99195960521b606082015260800190565b6020808252603e908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265206f66666572696e6720746f6b656e0000606082015260800190565b60208082526033908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a20436040820152721bdb9d1c9858dd081b9bdd08185b1b1bddd959606a1b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260439082015260008051602061329083398151915260408201527f6869732061646472657373206973206e6f7420696e207468652077686974656c6060820152621a5cdd60ea1b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205060408201526a1bdbdb081b9bdd081cd95d60aa1b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204d60408201527f757374206861766520616e206163746976652070726f66696c65000000000000606082015260800190565b60208082526029908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a204e6f6040820152687420666163746f727960b81b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526038908201527f49464f496e697469616c697a61626c653a3a7265636f76657257726f6e67546f60408201527f6b656e733a2043616e6e6f74206265204c5020746f6b656e0000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252602e908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a20506f6f6c2060408201526d191bd95cc81b9bdd08195e1a5cdd60921b606082015260800190565b6020808252603c9082015260008051602061329083398151915260408201527f6f6b656e73206e6f74206465706f73697465642070726f7065726c7900000000606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204160408201527006d6f756e74206d757374206265203e203607c1b606082015260800190565b6020808252605b908201526000805160206132b083398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f6520686967686572207468616e2063757272656e7420626c6f636b0000000000608082015260a00190565b60208082526032908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20446040820152716964206e6f7420706172746963697061746560701b606082015260800190565b60208082526027908201526000805160206132908339815191526040820152666f6f206c61746560c81b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b60208082526035908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a604082015274204e6f7420656e6f756768204c5020746f6b656e7360581b606082015260800190565b60208082526031908201527f49464f496e697469616c697a61626c653a3a696e697469616c697a653a20416c6040820152701c9958591e481a5b9a5d1a585b1a5e9959607a1b606082015260800190565b6020808252603b908201527f49464f496e697469616c697a61626c653a3a66696e616c57697468647261773a60408201527f204e6f7420656e6f756768206f66666572696e6720746f6b656e730000000000606082015260800190565b60208082526028908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a20546040820152676f6f206561726c7960c01b606082015260800190565b60208082526030908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201526f1bdb881d985b1a59081c1bdbdb081a5960821b606082015260800190565b6020808252602a908201527f49464f496e697469616c697a61626c653a3a736574506f6f6c3a2049464f2068604082015269185cc81cdd185c9d195960b21b606082015260800190565b60208082526039908201527f49464f496e697469616c697a61626c653a3a6e6f74436f6e74726163743a205060408201527f726f787920636f6e7472616374206e6f7420616c6c6f77656400000000000000606082015260800190565b6020808252603a908201526000805160206132b083398151915260408201527f456e64426c6f636b733a2049464f206861732073746172746564000000000000606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252603b908201526000805160206132b083398151915260408201527f456e64426c6f636b733a20456e64426c6f636b20746f6f206661720000000000606082015260800190565b60208082526054908201527f49464f496e697469616c697a61626c653a3a757064617465486172766573744260408201527f6c6f636b733a204e65772068617276657374426c6f636b206d75737420626520606082015273686967686572207468616e20656e64426c6f636b60601b608082015260a00190565b6020808252602b908201527f49464f496e697469616c697a61626c653a3a68617276657374506f6f6c3a204160408201526a6c726561647920646f6e6560a81b606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526028908201526000805160206132908339815191526040820152676f6f206561726c7960c01b606082015260800190565b60208082526059908201526000805160206132b083398151915260408201527f456e64426c6f636b733a204e6577207374617274426c6f636b206d757374206260608201527f65206c6f776572207468616e206e657720656e64426c6f636b00000000000000608082015260a00190565b60208082526036908201527f49464f496e697469616c697a61626c653a3a75706461746548617276657374426040820152751b1bd8dadcce88125193c81a185cc81cdd185c9d195960521b606082015260800190565b6020808252603a908201527f49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a204e60408201527f657720616d6f756e742061626f76652075736572206c696d6974000000000000606082015260800190565b8151815260208083015190820152604080830151908201526060808301511515908201526080808301519082015260a0808301519082015260c0808301516001600160a01b039081169183019190915260e09283015116918101919091526101000190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b928352602083019190915260ff16604082015260600190565b60ff91909116815260200190565b60005b83811015613270578181015183820152602001613258565b83811115611e0a5750506000910152565b8015158114611a3357600080fdfe49464f496e697469616c697a61626c653a3a6465706f736974506f6f6c3a205449464f496e697469616c697a61626c653a3a7570646174655374617274416e64a26469706673582212200fc44f4c45ad0b5a0349ada0a1ded444fbce43eeb4d1d3c4e2111ede6b269d2364736f6c634300060c0033a264697066735822122049f77587d12698f94d277b2044a2c3c170de59abc9e4c1cb19875e0ef6aa487064736f6c634300060c0033