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