Contract Address Details
contract

0x912F4d6607160256787a2AD40dA098Ac2aFE57AC

Contract Name
HoprStake
Creator
0x3da21e–7481e1 at 0x2fd7a3–fcb5b7
Balance
0 xDAI
Tokens
Fetching tokens...
Transactions
17,332 Transactions
Transfers
23,858 Transfers
Gas Used
2,163,136,959
Last Balance Update
22351440
Contract name:
HoprStake




Optimization enabled
false
Compiler version
v0.8.4+commit.c7e474f2




EVM Version
default




Verified at
2021-07-26T11:19:27.084811Z

Constructor Arguments

00000000000000000000000043d13d7b83607f14335cf2cb75e87da369d056c7000000000000000000000000a18732dc751be0db04157eb92c92ba9d0fc09fc5000000000000000000000000d057604a14982fe8d88c5fc25aac3267ea142a08000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c1

Arg [0] (address) : 0x43d13d7b83607f14335cf2cb75e87da369d056c7
Arg [1] (address) : 0xa18732dc751be0db04157eb92c92ba9d0fc09fc5
Arg [2] (address) : 0xd057604a14982fe8d88c5fc25aac3267ea142a08
Arg [3] (address) : 0xd4fdec44db9d44b8f2b6d529620f9c0c7066a2c1

              

Contract source code

// Sources flattened with hardhat v2.4.1 https://hardhat.org

// File @openzeppelin/contracts/token/ERC20/[email protected]

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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/utils/[email protected]


pragma solidity ^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;
        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");

        (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");

        (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");

        (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");

        (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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}


// File @openzeppelin/contracts/token/ERC20/utils/S[email protected]


pragma solidity ^0.8.0;


/**
 * @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 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'
        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) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}


// File @openzeppelin/contracts/token/ERC777/[email protected]


pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
 *
 * Accounts can be notified of {IERC777} tokens being sent to them by having a
 * contract implement this interface (contract holders can be their own
 * implementer) and registering it on the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
 *
 * See {IERC1820Registry} and {ERC1820Implementer}.
 */
interface IERC777Recipient {
    /**
     * @dev Called by an {IERC777} token contract whenever tokens are being
     * moved or created into a registered account (`to`). The type of operation
     * is conveyed by `from` being the zero address or not.
     *
     * This call occurs _after_ the token contract's state is updated, so
     * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
     *
     * This function may revert to prevent the operation from being executed.
     */
    function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external;
}


// File @openzeppelin/contracts/token/ERC721/[email protected]


pragma solidity ^0.8.0;

/**
 * @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/security/[email protected]


pragma solidity ^0.8.0;

/**
 * @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() {
        _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 @openzeppelin/contracts/utils/[email protected]


pragma solidity ^0.8.0;

/*
 * @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 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) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}


// File @openzeppelin/contracts/access/[email protected]


pragma solidity ^0.8.0;

/**
 * @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() {
        _setOwner(_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 {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}


// File @openzeppelin/contracts/utils/introspection/[email protected]


pragma solidity ^0.8.0;

/**
 * @dev Interface of the global ERC1820 Registry, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
 * implementers for interfaces in this registry, as well as query support.
 *
 * Implementers may be shared by multiple accounts, and can also implement more
 * than a single interface for each account. Contracts can implement interfaces
 * for themselves, but externally-owned accounts (EOA) must delegate this to a
 * contract.
 *
 * {IERC165} interfaces can also be queried via the registry.
 *
 * For an in-depth explanation and source code analysis, see the EIP text.
 */
interface IERC1820Registry {
    /**
     * @dev Sets `newManager` as the manager for `account`. A manager of an
     * account is able to set interface implementers for it.
     *
     * By default, each account is its own manager. Passing a value of `0x0` in
     * `newManager` will reset the manager to this initial state.
     *
     * Emits a {ManagerChanged} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     */
    function setManager(address account, address newManager) external;

    /**
     * @dev Returns the manager for `account`.
     *
     * See {setManager}.
     */
    function getManager(address account) external view returns (address);

    /**
     * @dev Sets the `implementer` contract as ``account``'s implementer for
     * `interfaceHash`.
     *
     * `account` being the zero address is an alias for the caller's address.
     * The zero address can also be used in `implementer` to remove an old one.
     *
     * See {interfaceHash} to learn how these are created.
     *
     * Emits an {InterfaceImplementerSet} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
     * end in 28 zeroes).
     * - `implementer` must implement {IERC1820Implementer} and return true when
     * queried for support, unless `implementer` is the caller. See
     * {IERC1820Implementer-canImplementInterfaceForAddress}.
     */
    function setInterfaceImplementer(
        address account,
        bytes32 _interfaceHash,
        address implementer
    ) external;

    /**
     * @dev Returns the implementer of `interfaceHash` for `account`. If no such
     * implementer is registered, returns the zero address.
     *
     * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
     * zeroes), `account` will be queried for support of it.
     *
     * `account` being the zero address is an alias for the caller's address.
     */
    function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);

    /**
     * @dev Returns the interface hash for an `interfaceName`, as defined in the
     * corresponding
     * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
     */
    function interfaceHash(string calldata interfaceName) external pure returns (bytes32);

    /**
     * @notice Updates the cache with whether the contract implements an ERC165 interface or not.
     * @param account Address of the contract for which to update the cache.
     * @param interfaceId ERC165 interface for which to update the cache.
     */
    function updateERC165Cache(address account, bytes4 interfaceId) external;

    /**
     * @notice Checks whether a contract implements an ERC165 interface or not.
     * If the result is not cached a direct lookup on the contract address is performed.
     * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
     * {updateERC165Cache} with the contract address.
     * @param account Address of the contract to check.
     * @param interfaceId ERC165 interface to check.
     * @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);

    /**
     * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
     * @param account Address of the contract to check.
     * @param interfaceId ERC165 interface to check.
     * @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);

    event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);

    event ManagerChanged(address indexed account, address indexed newManager);
}


// File @openzeppelin/contracts/utils/math/[email protected]


pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute.
        return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}


// File @openzeppelin/contracts/utils/introspection/[email protected]


pragma solidity ^0.8.0;

/**
 * @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/[email protected]


pragma solidity ^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 contracts/IHoprBoost.sol


pragma solidity ^0.8.0;

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IHoprBoost is IERC721 {
    /**
     * @dev Returns the boost factor and the redeem deadline associated with ``tokenId``.
     * @param tokenId uint256 token Id of the boost.
     */
    function boostOf(uint256 tokenId) external view returns (uint256, uint256);
    
    /**
     * @dev Returns the boost type index associated with ``tokenId``.
     * @param tokenId uint256 token Id of the boost.
     */
    function typeIndexOf(uint256 tokenId) external view returns (uint256);
}


// File contracts/HoprStake.sol


pragma solidity ^0.8.0;









/**
 * 
 */
contract HoprStake is Ownable, IERC777Recipient, IERC721Receiver, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using Math for uint256;

    struct Account {
        uint256 actualLockedTokenAmount; // The amount of LOCK_TOKEN being actually locked to the contract. 
                                         // Those tokens can be withdrawn after “UNLOCK_START”
        uint256 virtualLockedTokenAmount; // The amount of LOCK_TOKEN token being virtually locked to the contract. 
                                          // This field is only relevant to seed investors. Those tokens cannot be withdrawn after “UNLOCK_START”.
        uint256 lastSyncTimestamp; // Timestamp at which any “Account” attribute gets synced for the last time. 
        uint256 cumulatedRewards; // Rewards accredited to the account at “lastSyncTimestamp”.
        uint256 claimedRewards; // Rewards claimed by the account.
    }

    uint256 public constant BASIC_START = 1627387200; // Block timestamp at which incentive program starts for accounts that stake real LOCK_TOKEN. Default value is 1627387200 (July 27th 2021 14:00 CET).
    uint256 public constant SEED_START = 1630065600; // Block timestamp at which incentive program starts for seed investors that promise to stake their unreleased tokens. Default value is 1630065600 (August 27th 2021 14:00 CET).
    uint256 public constant PROGRAM_END = 1642424400; // Block timestamp at which incentive program ends. From this timestamp on, tokens can be unlocked. Default value is 1642424400 (Jan 17th 2022 14:00 CET).
    uint256 public constant FACTOR_DENOMINATOR = 1e12; // Denominator of the “Basic reward factor”. Default value is 1e12.
    uint256 public constant BASIC_FACTOR_NUMERATOR = 5787; // Numerator of the “Basic reward factor”, for all accounts (except for seed investors) that participate in the program. Default value is 5787, which corresponds to 5.787/1e9 per second. Its associated denominator is FACTOR_DENOMINATOR. 
    uint256 public constant SEED_FACTOR_NUMERATOR = 7032; // Numerator of the "Seed reward factor”, for all accounts (except for seed investors) that participate in the program. Default value is 7032, which corresponds to 7.032/1e9 per second. Its associated denominator is FACTOR_DENOMINATOR. 
    uint256 public constant BOOST_CAP = 1e24; // Cap on actual locked tokens for receiving additional boosts.
    address public LOCK_TOKEN = 0xD057604A14982FE8D88c5fC25Aac3267eA142a08; // Token that HOPR holders need to lock to the contract: xHOPR address.
    address public REWARD_TOKEN = 0xD4fdec44DB9D44B8f2b6d529620f9C0C7066A2c1; // Token that HOPR holders can claim as rewards: wxHOPR address

    IHoprBoost public nftContract; // Address of the NFT smart contract.
    mapping(address=>mapping(uint256=>uint256)) public redeemedNft; // Redeemed NFT per account, structured as “account -> index -> NFT tokenId”.
    mapping(address=>uint256) public redeemedNftIndex; // The last index of redeemed NFT of an account. It defines the length of the “redeemedBoostToken mapping.
    mapping(address=>mapping(uint256=>uint256)) public redeemedFactor; // Redeemed boost factor per account, structured as “account -> index -> NFT tokenId”.
    mapping(address=>uint256) public redeemedFactorIndex; // The last index of redeemed boost factor factor of an account. It defines the length of the “redeemedFactor” mapping.

    mapping(address=>Account) public accounts; // It stores the locked token amount, earned and claimed rewards per account.
    uint256 public totalLocked;  // Total amount of tokens being locked in the incentive program. Virtual token locks are not taken into account.
    uint256 public availableReward; // Total amount of reward tokens currently available in the lock.

    // setup ERC1820
    IERC1820Registry private constant ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
    bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");

    event Sync(address indexed account, uint256 indexed increment);
    event Staked(address indexed account, uint256 indexed actualAmount, uint256 indexed virtualAmount);
    event Released(address indexed account, uint256 indexed actualAmount, uint256 indexed virtualAmount);
    event RewardFueled(uint256 indexed amount);
    event Redeemed(address indexed account, uint256 indexed boostTokenId, bool indexed factorRegistered);
    event Claimed(address indexed account, uint256 indexed rewardAmount);

    /**
     * @dev Provide NFT contract address. Transfer owner role to the new owner address. 
     * At deployment, it also registers the lock contract as an ERC777 recipient. 
     * @param _nftAddress address Address of the NFT contract.
     * @param _newOwner address Address of the new owner. This new owner can reclaim any ERC20 and ERC721 token being accidentally sent to the lock contract. 
     */
    constructor(address _nftAddress, address _newOwner, address _lockToken, address _rewardToken) {
        // implement in favor of testing
        uint chainId;
        assembly {
            chainId := chainid()
        }
        if (chainId != 100) {
            LOCK_TOKEN = _lockToken;
            REWARD_TOKEN = _rewardToken; 
        }
        nftContract = IHoprBoost(_nftAddress);
        transferOwnership(_newOwner);
        ERC1820_REGISTRY.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
    }

    /**
     * @dev ERC677 hook. Token holders can send their tokens with `transferAndCall` to the stake contract.  
     * After PROGRAM_END, it refuses tokens; Before PROGRAM_END, it accepts tokens xHOPR token, sync 
     * Account state, and update totalLocked. 
     * @param _from address Address of tokens sender
     * @param _value uint256 token amount being transferred
     * @param _data bytes Data being sent along with token transfer
     */
    function onTokenTransfer(
        address _from, 
        uint256 _value, 
        // solhint-disable-next-line no-unused-vars
        bytes memory _data
    ) external returns (bool) {
        require(msg.sender == LOCK_TOKEN, "HoprStake: Only accept LOCK_TOKEN in staking");
        require(block.timestamp <= PROGRAM_END, "HoprStake: Program ended, cannot stake anymore.");

        _sync(_from);
        accounts[_from].actualLockedTokenAmount += _value;
        totalLocked += _value;
        emit Staked(_from, _value, 0);

        return true;
    }

    /**
     * @dev ERC777 hook. To receive wxHOPR to fuel the reward pool with `send()` method. It updates the availableReward by tokenAmount.
     * @param operator address operator requesting the transfer
     * @param from address token holder address
     * @param to address recipient address
     * @param amount uint256 amount of tokens to transfer
     * @param userData bytes hex information provided by the token holder (if any)
     * @param operatorData bytes extra information provided by the operator (if any)
     */
    function tokensReceived(
        // solhint-disable-next-line no-unused-vars
        address operator,
        address from,
        address to,
        uint256 amount,
        // solhint-disable-next-line no-unused-vars
        bytes calldata userData,
        // solhint-disable-next-line no-unused-vars
        bytes calldata operatorData
    ) external override {
        require(msg.sender == REWARD_TOKEN, "HoprStake: Sender must be wxHOPR token");
        require(to == address(this), "HoprStake: Must be sending tokens to HoprStake contract");
        require(from == owner(), "HoprStake: Only accept owner to provide rewards");
        availableReward += amount;
        emit RewardFueled(amount);
    }

    /**
     * @dev Whenever a boost `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * when redeeming, this function is called. Boost factor associated with the 
     * It must return its Solidity selector to confirm the token transfer upon success.
     * @param operator address operator requesting the transfer
     * @param from address token holder address
     * @param tokenId uint256 amount of tokens to transfer
     * @param data bytes hex information provided by the token holder (if any)
     */
    function onERC721Received(
        // solhint-disable-next-line no-unused-vars
        address operator,
        address from,
        uint256 tokenId,
        // solhint-disable-next-line no-unused-vars
        bytes calldata data
    ) external override returns (bytes4) {
        require(_msgSender() == address(nftContract), "HoprStake: Cannot SafeTransferFrom tokens other than HoprBoost.");
        require(block.timestamp <= PROGRAM_END, "HoprStake: Program ended, cannot redeem boosts.");
        // Account memory account = accounts[from];
        _sync(from);

        // redeem NFT
        redeemedNft[from][redeemedNftIndex[from]] = tokenId;
        redeemedNftIndex[from] += 1;

        // update boost factor
        uint256 typeId = nftContract.typeIndexOf(tokenId);
        (uint256 factor, uint256 deadline) = nftContract.boostOf(tokenId);
        require(block.timestamp <= deadline, "HoprStake: Cannot redeem an expired boost.");

        uint256 boostIndex = redeemedFactorIndex[from];
        uint256 index = 0;
        for (index; index < boostIndex; index++) {
            // loop through redeemed factors, replace the factor of the same type, if the current factor is larger.
            uint256 redeemedId = redeemedFactor[from][index];
            (uint256 redeemedFactorValue, ) = nftContract.boostOf(redeemedId);

            if (nftContract.typeIndexOf(redeemedId) == typeId) {
                if (redeemedFactorValue < factor) {
                    redeemedFactor[from][index] = tokenId;
                }
                emit Redeemed(from, tokenId, redeemedFactorValue < factor);
                break;
            }
        }
        if (index == boostIndex) {
            // new type being redeemed.
            redeemedFactor[from][boostIndex] = tokenId;
            redeemedFactorIndex[from] += 1;
            emit Redeemed(from, tokenId, true);
        }

        return IERC721Receiver(address(this)).onERC721Received.selector;
    }

    /**
    * @dev Only owner can call this function to store virtual lock for seed investors. 
    * If the investor hasn't locked any token in this account, create an "Account" with {0, caps[i], block.timestamp, 0, 0}. 
    * If the investor has locked some tokens in this account, update its “virtualLockedTokenAmount”.
    * This function can be called at anytime of the program.
    * @param investors address[] Array of seed investors accounts.
    * @param caps uint256[] Array of their virtually locked tokens.
    */
    function lock(address[] calldata investors, uint256[] calldata caps) onlyOwner external {
        require(block.timestamp <= PROGRAM_END, "HoprStake: Program ended, cannot stake anymore.");
        require(investors.length == caps.length, "HoprStake: Length does not match");

        for (uint256 index = 0; index < investors.length; index++) { 
            address investor = investors[index];
            _sync(investor);
            accounts[investor].virtualLockedTokenAmount += caps[index];
            // accounts[investor].lastSyncTimestamp = block.timestamp;
            emit Staked(investor, 0, caps[index]);
        }
    }

    /**
     * @dev Manually sync account's reward states
     * @notice public function of ``_sync``.
     * @param account address Account whose stake rewards will be synced.
     */
    function sync(address account) external {
        _sync(account); 
    }

    /**
     * @dev Sync rewards and claim them
     * @notice public function of ``_sync`` + ``_claim``
     * @param account address Account whose stake rewards will be synced and claimed.
     */
    function claimRewards(address account) public {
        _sync(account); 
        _claim(account);
    }

    /**
     * @dev Unlock staking
     * @param account address Account that staked tokens.
     */
    function unlock(address account) external {
        require(block.timestamp > PROGRAM_END, "HoprStake: Program is ongoing, cannot unlock stake.");
        uint256 actualStake = accounts[account].actualLockedTokenAmount;
        uint256 virtualStake = accounts[account].virtualLockedTokenAmount;
        _sync(account); 
        accounts[account].actualLockedTokenAmount = 0;
        accounts[account].virtualLockedTokenAmount = 0;
        totalLocked -= actualStake;
        _claim(account);
        // unlock actual staked tokens
        IERC20(LOCK_TOKEN).safeTransfer(account, actualStake);
        // unlock redeemed NFTs
        for (uint256 index = 0; index < redeemedNftIndex[account]; index++) {
            nftContract.transferFrom(address(this), account, redeemedNft[account][index]);
        }
        emit Released(account, actualStake, virtualStake);
    }

    /**
     * @dev Reclaim any ERC20 token being accidentally sent to the contract.
     * @param tokenAddress address ERC20 token address.
     */
    function reclaimErc20Tokens(address tokenAddress) external onlyOwner nonReentrant {
        uint256 difference;
        if (tokenAddress == LOCK_TOKEN) {
            difference = IERC20(LOCK_TOKEN).balanceOf(address(this)) - totalLocked;
        } else {
            difference = IERC20(tokenAddress).balanceOf(address(this));
        }
        IERC20(tokenAddress).safeTransfer(owner(), difference);
    }

    /**
     * @dev Reclaim any ERC721 token being accidentally sent to the contract.
     * @param tokenAddress address ERC721 token address.
     */
    function reclaimErc721Tokens(address tokenAddress, uint256 tokenId) external onlyOwner nonReentrant {
        IHoprBoost(tokenAddress).transferFrom(address(this), owner(), tokenId);
    }

    /**
     * @dev Returns the increment of cumulated rewards during the “lastSyncTimestamp” and current block.timestamp. 
     * @param _account address Address of the account whose rewards will be calculated.
     */
    function getCumulatedRewardsIncrement(address _account) public view returns (uint256) {
        return _getCumulatedRewardsIncrement(_account);
    }

    /**
     * @dev Calculates the increment of cumulated rewards during the “lastSyncTimestamp” and block.timestamp. 
     * current block timestamp and lastSyncTimestamp are confined in [BASIC_START, PROGRAMEND] for basic and boosted lockup,
     * and [SEED_START, PROGRAMEND] for seed investors.
     * @param _account address Address of the account whose rewards will be calculated.
     */
    function _getCumulatedRewardsIncrement(address _account) private view returns (uint256) {
        Account memory account = accounts[_account];
        if (block.timestamp <= BASIC_START || account.lastSyncTimestamp >= PROGRAM_END) {
            // skip calculation and return directly 0;
            return 0;
        }
        // Per second gain, for basic lock-up.
        uint256 gainPerSec = account.actualLockedTokenAmount * BASIC_FACTOR_NUMERATOR;
        
        // Per second gain, for additional boost, applicable to amount under BOOST_CAP
        for (uint256 index = 0; index < redeemedFactorIndex[_account]; index++) {
            uint256 tokenId = redeemedFactor[_account][index];
            (uint256 boost, ) = nftContract.boostOf(tokenId);
            gainPerSec += (account.actualLockedTokenAmount.min(BOOST_CAP)) * boost;
        }

        return (
                gainPerSec * (
                block.timestamp.max(BASIC_START).min(PROGRAM_END) - 
                account.lastSyncTimestamp.max(BASIC_START).min(PROGRAM_END)
                ) + account.virtualLockedTokenAmount * SEED_FACTOR_NUMERATOR * (    // Per second gain, for seed investor lock-up
                block.timestamp.max(SEED_START).min(PROGRAM_END) - 
                account.lastSyncTimestamp.max(SEED_START).min(PROGRAM_END)
                )
            ) / FACTOR_DENOMINATOR;
    }

    /**
     * @dev Update “lastSyncTimestamp” with the current block timestamp and update “cumulatedRewards” with _getCumulatedRewardsIncrement(account) 
     * @param _account address Address of the account whose rewards will be calculated.
     */
    function _sync(address _account) private {
        uint256 increment = _getCumulatedRewardsIncrement(_account);
        accounts[_account].cumulatedRewards += increment;
        accounts[_account].lastSyncTimestamp = block.timestamp;
        emit Sync(_account, increment);
    }

    /**
     * @dev Claim rewards for staking.
     * @param _account address Address of the staking account.
     */
    function _claim(address _account) private {
        Account memory account = accounts[_account];
        // update states
        uint256 amount = account.cumulatedRewards - account.claimedRewards;
        require(amount > 0, "HoprStake: Nothing to claim");
        accounts[_account].claimedRewards = accounts[_account].cumulatedRewards;
        require(availableReward >= amount, "HoprStake: Insufficient reward pool.");
        availableReward -= amount;
        // send rewards to the account.
        IERC20(REWARD_TOKEN).safeTransfer(_account, amount);
        emit Claimed(_account, amount);
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_nftAddress","internalType":"address"},{"type":"address","name":"_newOwner","internalType":"address"},{"type":"address","name":"_lockToken","internalType":"address"},{"type":"address","name":"_rewardToken","internalType":"address"}]},{"type":"event","name":"Claimed","inputs":[{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"rewardAmount","internalType":"uint256","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":"event","name":"Redeemed","inputs":[{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"boostTokenId","internalType":"uint256","indexed":true},{"type":"bool","name":"factorRegistered","internalType":"bool","indexed":true}],"anonymous":false},{"type":"event","name":"Released","inputs":[{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"actualAmount","internalType":"uint256","indexed":true},{"type":"uint256","name":"virtualAmount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"RewardFueled","inputs":[{"type":"uint256","name":"amount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"Staked","inputs":[{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"actualAmount","internalType":"uint256","indexed":true},{"type":"uint256","name":"virtualAmount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"Sync","inputs":[{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"increment","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BASIC_FACTOR_NUMERATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BASIC_START","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BOOST_CAP","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FACTOR_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"LOCK_TOKEN","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PROGRAM_END","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"REWARD_TOKEN","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"SEED_FACTOR_NUMERATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"SEED_START","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"actualLockedTokenAmount","internalType":"uint256"},{"type":"uint256","name":"virtualLockedTokenAmount","internalType":"uint256"},{"type":"uint256","name":"lastSyncTimestamp","internalType":"uint256"},{"type":"uint256","name":"cumulatedRewards","internalType":"uint256"},{"type":"uint256","name":"claimedRewards","internalType":"uint256"}],"name":"accounts","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"availableReward","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimRewards","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCumulatedRewardsIncrement","inputs":[{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"lock","inputs":[{"type":"address[]","name":"investors","internalType":"address[]"},{"type":"uint256[]","name":"caps","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IHoprBoost"}],"name":"nftContract","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"operator","internalType":"address"},{"type":"address","name":"from","internalType":"address"},{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"onTokenTransfer","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_value","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reclaimErc20Tokens","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reclaimErc721Tokens","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"redeemedFactor","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"redeemedFactorIndex","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"redeemedNft","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"redeemedNftIndex","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sync","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tokensReceived","inputs":[{"type":"address","name":"operator","internalType":"address"},{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"userData","internalType":"bytes"},{"type":"bytes","name":"operatorData","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalLocked","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unlock","inputs":[{"type":"address","name":"account","internalType":"address"}]}]
            

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101d95760003560e01c806370af093411610104578063cbffa3c7116100a2578063ef0526a211610071578063ef0526a21461055e578063ef5cfb8c1461057c578063f20c912414610598578063f2fde38b146105b6576101d9565b8063cbffa3c7146104d4578063d0c02d63146104f2578063d0da680114610522578063d56d229d14610540576101d9565b80638da5cb5b116100de5780638da5cb5b1461044c57806399248ea71461046a578063a4c0ed3614610488578063a5841194146104b8576101d9565b806370af0934146103e2578063715018a61461041257806381128c1d1461041c576101d9565b806348c64e411161017c5780635e5c06e21161014b5780635e5c06e2146103565780635ef73d581461038a5780636067bc15146103a85780636aa8d4cf146103c4576101d9565b806348c64e41146102e25780634ad84b34146102fe578063568914121461031c5780635c3c71f41461033a576101d9565b8063150b7a02116101b8578063150b7a02146102485780631f014d83146102785780632f6c493c146102a85780632f998468146102c4576101d9565b806223de29146101de5780630a1a257a146101fa578063112376dc1461022a575b600080fd5b6101f860048036038101906101f39190612a54565b6105d2565b005b610214600480360381019061020f9190612b97565b610795565b60405161022191906133ad565b60405180910390f35b6102326107ba565b60405161023f91906133ad565b60405180910390f35b610262600480360381019061025d9190612b17565b6107c2565b60405161026f9190613115565b60405180910390f35b610292600480360381019061028d9190612a2b565b610f16565b60405161029f91906133ad565b60405180910390f35b6102c260048036038101906102bd9190612a2b565b610f2e565b005b6102cc611293565b6040516102d991906133ad565b60405180910390f35b6102fc60048036038101906102f79190612b97565b611299565b005b6103066113e4565b60405161031391906133ad565b60405180910390f35b6103246113ea565b60405161033191906133ad565b60405180910390f35b610354600480360381019061034f9190612c3a565b6113f0565b005b610370600480360381019061036b9190612a2b565b61169c565b6040516103819594939291906133c8565b60405180910390f35b6103926116d2565b60405161039f91906133ad565b60405180910390f35b6103c260048036038101906103bd9190612a2b565b6116db565b005b6103cc611985565b6040516103d991906133ad565b60405180910390f35b6103fc60048036038101906103f79190612a2b565b61198b565b60405161040991906133ad565b60405180910390f35b61041a61199d565b005b61043660048036038101906104319190612b97565b611a25565b60405161044391906133ad565b60405180910390f35b610454611a4a565b604051610461919061307f565b60405180910390f35b610472611a73565b60405161047f919061307f565b60405180910390f35b6104a2600480360381019061049d9190612bd3565b611a99565b6040516104af91906130fa565b60405180910390f35b6104d260048036038101906104cd9190612a2b565b611c3e565b005b6104dc611c4a565b6040516104e991906133ad565b60405180910390f35b61050c60048036038101906105079190612a2b565b611c52565b60405161051991906133ad565b60405180910390f35b61052a611c6a565b604051610537919061307f565b60405180910390f35b610548611c90565b6040516105559190613130565b60405180910390f35b610566611cb6565b60405161057391906133ad565b60405180910390f35b61059660048036038101906105919190612a2b565b611cc4565b005b6105a0611cd9565b6040516105ad91906133ad565b60405180910390f35b6105d060048036038101906105cb9190612a2b565b611ce1565b005b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610662576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106599061320d565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146106d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c79061322d565b60405180910390fd5b6106d8611a4a565b73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610745576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073c9061324d565b60405180910390fd5b84600b600082825461075791906134a3565b92505081905550847f2bf52bcae319602514e02ff69bbe4b89a19718b96e7867044128ec872419437c60405160405180910390a25050505050505050565b6005602052816000526040600020602052806000526040600020600091509150505481565b636128d3c081565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610805611dd9565b73ffffffffffffffffffffffffffffffffffffffff161461085b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108529061318d565b60405180910390fd5b6361e568504211156108a2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610899906131cd565b60405180910390fd5b6108ab85611de1565b83600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000600660008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548152602001908152602001600020819055506001600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461098f91906134a3565b925050819055506000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663562317c5866040518263ffffffff1660e01b81526004016109f391906133ad565b60206040518083038186803b158015610a0b57600080fd5b505afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a439190612cd8565b9050600080600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663896cddf4886040518263ffffffff1660e01b8152600401610aa391906133ad565b604080518083038186803b158015610aba57600080fd5b505afa158015610ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af29190612d01565b9150915080421115610b39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b30906132ad565b60405180910390fd5b6000600860008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060005b81811015610e01576000600760008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000205490506000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663896cddf4836040518263ffffffff1660e01b8152600401610c3a91906133ad565b604080518083038186803b158015610c5157600080fd5b505afa158015610c65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c899190612d01565b50905086600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663562317c5846040518263ffffffff1660e01b8152600401610ce891906133ad565b60206040518083038186803b158015610d0057600080fd5b505afa158015610d14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d389190612cd8565b1415610dec5785811015610d9c578a600760008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858152602001908152602001600020819055505b85811015158b8d73ffffffffffffffffffffffffffffffffffffffff167f066d96cb280fccf3a0a3a5686966a801b2690a32ec98fd2711a4e09f345d935d60405160405180910390a45050610e01565b50508080610df9906136c3565b915050610b80565b81811415610efe5788600760008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000848152602001908152602001600020819055506001600860008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610eae91906134a3565b9250508190555060011515898b73ffffffffffffffffffffffffffffffffffffffff167f066d96cb280fccf3a0a3a5686966a801b2690a32ec98fd2711a4e09f345d935d60405160405180910390a45b63150b7a0260e01b9550505050505095945050505050565b60086020528060005260406000206000915090505481565b6361e568504211610f74576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6b906132ed565b60405180910390fd5b6000600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015490506000600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905061100b83611de1565b6000600960008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001819055506000600960008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555081600a60008282546110ad9190613584565b925050819055506110bd83611ed6565b61110a8383600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661212e9092919063ffffffff16565b60005b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481101561124857600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd3086600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868152602001908152602001600020546040518463ffffffff1660e01b81526004016112039392919061309a565b600060405180830381600087803b15801561121d57600080fd5b505af1158015611231573d6000803e3d6000fd5b505050508080611240906136c3565b91505061110d565b5080828473ffffffffffffffffffffffffffffffffffffffff167f82e416ba72d10e709b5de7ac16f5f49ff1d94f22d55bf582d353d3c313a1e8dd60405160405180910390a4505050565b611b7881565b6112a1611dd9565b73ffffffffffffffffffffffffffffffffffffffff166112bf611a4a565b73ffffffffffffffffffffffffffffffffffffffff1614611315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161130c9061328d565b60405180910390fd5b6002600154141561135b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113529061336d565b60405180910390fd5b60026001819055508173ffffffffffffffffffffffffffffffffffffffff166323b872dd30611388611a4a565b846040518463ffffffff1660e01b81526004016113a79392919061309a565b600060405180830381600087803b1580156113c157600080fd5b505af11580156113d5573d6000803e3d6000fd5b50505050600180819055505050565b600b5481565b600a5481565b6113f8611dd9565b73ffffffffffffffffffffffffffffffffffffffff16611416611a4a565b73ffffffffffffffffffffffffffffffffffffffff161461146c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114639061328d565b60405180910390fd5b6361e568504211156114b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114aa9061326d565b60405180910390fd5b8181905084849050146114fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f2906131ad565b60405180910390fd5b60005b84849050811015611695576000858583818110611544577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90506020020160208101906115599190612a2b565b905061156481611de1565b83838381811061159d577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90506020020135600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008282546115f591906134a3565b92505081905550838383818110611635577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9050602002013560008273ffffffffffffffffffffffffffffffffffffffff167f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee9060405160405180910390a450808061168d906136c3565b9150506114fe565b5050505050565b60096020528060005260406000206000915090508060000154908060010154908060020154908060030154908060040154905085565b64e8d4a5100081565b6116e3611dd9565b73ffffffffffffffffffffffffffffffffffffffff16611701611a4a565b73ffffffffffffffffffffffffffffffffffffffff1614611757576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161174e9061328d565b60405180910390fd5b6002600154141561179d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117949061336d565b60405180910390fd5b60026001819055506000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156118bc57600a54600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161185b919061307f565b60206040518083038186803b15801561187357600080fd5b505afa158015611887573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ab9190612cd8565b6118b59190613584565b9050611948565b8173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016118f5919061307f565b60206040518083038186803b15801561190d57600080fd5b505afa158015611921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119459190612cd8565b90505b61197a611953611a4a565b828473ffffffffffffffffffffffffffffffffffffffff1661212e9092919063ffffffff16565b506001808190555050565b61169b81565b6000611996826121b4565b9050919050565b6119a5611dd9565b73ffffffffffffffffffffffffffffffffffffffff166119c3611a4a565b73ffffffffffffffffffffffffffffffffffffffff1614611a19576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a109061328d565b60405180910390fd5b611a23600061252d565b565b6007602052816000526040600020602052806000526040600020600091509150505481565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b2b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b229061338d565b60405180910390fd5b6361e56850421115611b72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b699061326d565b60405180910390fd5b611b7b84611de1565b82600960008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000828254611bcd91906134a3565b9250508190555082600a6000828254611be691906134a3565b925050819055506000838573ffffffffffffffffffffffffffffffffffffffff167f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee9060405160405180910390a4600190509392505050565b611c4781611de1565b50565b6361e5685081565b60066020528060005260406000206000915090505481565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b69d3c21bcecceda100000081565b611ccd81611de1565b611cd681611ed6565b50565b6360fff54081565b611ce9611dd9565b73ffffffffffffffffffffffffffffffffffffffff16611d07611a4a565b73ffffffffffffffffffffffffffffffffffffffff1614611d5d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d549061328d565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611dcd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dc49061316d565b60405180910390fd5b611dd68161252d565b50565b600033905090565b6000611dec826121b4565b905080600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003016000828254611e4091906134a3565b9250508190555042600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020181905550808273ffffffffffffffffffffffffffffffffffffffff167f99869d968ca3581a661f31abb3a6aa70ccec5cdc49855eab174cf9e00a2462db60405160405180910390a35050565b6000600960008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060a0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815250509050600081608001518260600151611f6c9190613584565b905060008111611fb1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fa8906132cd565b60405180910390fd5b600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030154600960008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206004018190555080600b54101561207f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120769061332d565b60405180910390fd5b80600b60008282546120919190613584565b925050819055506120e58382600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661212e9092919063ffffffff16565b808373ffffffffffffffffffffffffffffffffffffffff167fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a60405160405180910390a3505050565b6121af8363a9059cbb60e01b848460405160240161214d9291906130d1565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506125f1565b505050565b600080600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505090506360fff5404211158061225057506361e56850816040015110155b1561225f576000915050612528565b600061169b8260000151612273919061352a565b905060005b600860008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054811015612411576000600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000205490506000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663896cddf4836040518263ffffffff1660e01b815260040161237191906133ad565b604080518083038186803b15801561238857600080fd5b505afa15801561239c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c09190612d01565b509050806123e569d3c21bcecceda100000087600001516126b890919063ffffffff16565b6123ef919061352a565b846123fa91906134a3565b935050508080612409906136c3565b915050612278565b5064e8d4a510006124496361e5685061243b636128d3c086604001516126d190919063ffffffff16565b6126b890919063ffffffff16565b6124766361e56850612468636128d3c0426126d190919063ffffffff16565b6126b890919063ffffffff16565b6124809190613584565b611b788460200151612492919061352a565b61249c919061352a565b6124cd6361e568506124bf6360fff54087604001516126d190919063ffffffff16565b6126b890919063ffffffff16565b6124fa6361e568506124ec6360fff540426126d190919063ffffffff16565b6126b890919063ffffffff16565b6125049190613584565b8361250f919061352a565b61251991906134a3565b61252391906134f9565b925050505b919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6000612653826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166126eb9092919063ffffffff16565b90506000815111156126b357808060200190518101906126739190612caf565b6126b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016126a99061334d565b60405180910390fd5b5b505050565b60008183106126c757816126c9565b825b905092915050565b6000818310156126e157816126e3565b825b905092915050565b60606126fa8484600085612703565b90509392505050565b606082471015612748576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161273f906131ed565b60405180910390fd5b61275185612817565b612790576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127879061330d565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516127b99190613068565b60006040518083038185875af1925050503d80600081146127f6576040519150601f19603f3d011682016040523d82523d6000602084013e6127fb565b606091505b509150915061280b82828661282a565b92505050949350505050565b600080823b905060008111915050919050565b6060831561283a5782905061288a565b60008351111561284d5782518084602001fd5b816040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612881919061314b565b60405180910390fd5b9392505050565b60006128a461289f84613440565b61341b565b9050828152602081018484840111156128bc57600080fd5b6128c7848285613650565b509392505050565b6000813590506128de81613c7a565b92915050565b60008083601f8401126128f657600080fd5b8235905067ffffffffffffffff81111561290f57600080fd5b60208301915083602082028301111561292757600080fd5b9250929050565b60008083601f84011261294057600080fd5b8235905067ffffffffffffffff81111561295957600080fd5b60208301915083602082028301111561297157600080fd5b9250929050565b60008151905061298781613c91565b92915050565b60008083601f84011261299f57600080fd5b8235905067ffffffffffffffff8111156129b857600080fd5b6020830191508360018202830111156129d057600080fd5b9250929050565b600082601f8301126129e857600080fd5b81356129f8848260208601612891565b91505092915050565b600081359050612a1081613ca8565b92915050565b600081519050612a2581613ca8565b92915050565b600060208284031215612a3d57600080fd5b6000612a4b848285016128cf565b91505092915050565b60008060008060008060008060c0898b031215612a7057600080fd5b6000612a7e8b828c016128cf565b9850506020612a8f8b828c016128cf565b9750506040612aa08b828c016128cf565b9650506060612ab18b828c01612a01565b955050608089013567ffffffffffffffff811115612ace57600080fd5b612ada8b828c0161298d565b945094505060a089013567ffffffffffffffff811115612af957600080fd5b612b058b828c0161298d565b92509250509295985092959890939650565b600080600080600060808688031215612b2f57600080fd5b6000612b3d888289016128cf565b9550506020612b4e888289016128cf565b9450506040612b5f88828901612a01565b935050606086013567ffffffffffffffff811115612b7c57600080fd5b612b888882890161298d565b92509250509295509295909350565b60008060408385031215612baa57600080fd5b6000612bb8858286016128cf565b9250506020612bc985828601612a01565b9150509250929050565b600080600060608486031215612be857600080fd5b6000612bf6868287016128cf565b9350506020612c0786828701612a01565b925050604084013567ffffffffffffffff811115612c2457600080fd5b612c30868287016129d7565b9150509250925092565b60008060008060408587031215612c5057600080fd5b600085013567ffffffffffffffff811115612c6a57600080fd5b612c76878288016128e4565b9450945050602085013567ffffffffffffffff811115612c9557600080fd5b612ca18782880161292e565b925092505092959194509250565b600060208284031215612cc157600080fd5b6000612ccf84828501612978565b91505092915050565b600060208284031215612cea57600080fd5b6000612cf884828501612a16565b91505092915050565b60008060408385031215612d1457600080fd5b6000612d2285828601612a16565b9250506020612d3385828601612a16565b9150509250929050565b612d46816135b8565b82525050565b612d55816135ca565b82525050565b612d64816135d6565b82525050565b6000612d7582613471565b612d7f8185613487565b9350612d8f81856020860161365f565b80840191505092915050565b612da48161362c565b82525050565b6000612db58261347c565b612dbf8185613492565b9350612dcf81856020860161365f565b612dd881613799565b840191505092915050565b6000612df0602683613492565b9150612dfb826137aa565b604082019050919050565b6000612e13603f83613492565b9150612e1e826137f9565b604082019050919050565b6000612e36602083613492565b9150612e4182613848565b602082019050919050565b6000612e59602f83613492565b9150612e6482613871565b604082019050919050565b6000612e7c602683613492565b9150612e87826138c0565b604082019050919050565b6000612e9f602683613492565b9150612eaa8261390f565b604082019050919050565b6000612ec2603783613492565b9150612ecd8261395e565b604082019050919050565b6000612ee5602f83613492565b9150612ef0826139ad565b604082019050919050565b6000612f08602f83613492565b9150612f13826139fc565b604082019050919050565b6000612f2b602083613492565b9150612f3682613a4b565b602082019050919050565b6000612f4e602a83613492565b9150612f5982613a74565b604082019050919050565b6000612f71601b83613492565b9150612f7c82613ac3565b602082019050919050565b6000612f94603383613492565b9150612f9f82613aec565b604082019050919050565b6000612fb7601d83613492565b9150612fc282613b3b565b602082019050919050565b6000612fda602483613492565b9150612fe582613b64565b604082019050919050565b6000612ffd602a83613492565b915061300882613bb3565b604082019050919050565b6000613020601f83613492565b915061302b82613c02565b602082019050919050565b6000613043602c83613492565b915061304e82613c2b565b604082019050919050565b61306281613622565b82525050565b60006130748284612d6a565b915081905092915050565b60006020820190506130946000830184612d3d565b92915050565b60006060820190506130af6000830186612d3d565b6130bc6020830185612d3d565b6130c96040830184613059565b949350505050565b60006040820190506130e66000830185612d3d565b6130f36020830184613059565b9392505050565b600060208201905061310f6000830184612d4c565b92915050565b600060208201905061312a6000830184612d5b565b92915050565b60006020820190506131456000830184612d9b565b92915050565b600060208201905081810360008301526131658184612daa565b905092915050565b6000602082019050818103600083015261318681612de3565b9050919050565b600060208201905081810360008301526131a681612e06565b9050919050565b600060208201905081810360008301526131c681612e29565b9050919050565b600060208201905081810360008301526131e681612e4c565b9050919050565b6000602082019050818103600083015261320681612e6f565b9050919050565b6000602082019050818103600083015261322681612e92565b9050919050565b6000602082019050818103600083015261324681612eb5565b9050919050565b6000602082019050818103600083015261326681612ed8565b9050919050565b6000602082019050818103600083015261328681612efb565b9050919050565b600060208201905081810360008301526132a681612f1e565b9050919050565b600060208201905081810360008301526132c681612f41565b9050919050565b600060208201905081810360008301526132e681612f64565b9050919050565b6000602082019050818103600083015261330681612f87565b9050919050565b6000602082019050818103600083015261332681612faa565b9050919050565b6000602082019050818103600083015261334681612fcd565b9050919050565b6000602082019050818103600083015261336681612ff0565b9050919050565b6000602082019050818103600083015261338681613013565b9050919050565b600060208201905081810360008301526133a681613036565b9050919050565b60006020820190506133c26000830184613059565b92915050565b600060a0820190506133dd6000830188613059565b6133ea6020830187613059565b6133f76040830186613059565b6134046060830185613059565b6134116080830184613059565b9695505050505050565b6000613425613436565b90506134318282613692565b919050565b6000604051905090565b600067ffffffffffffffff82111561345b5761345a61376a565b5b61346482613799565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006134ae82613622565b91506134b983613622565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156134ee576134ed61370c565b5b828201905092915050565b600061350482613622565b915061350f83613622565b92508261351f5761351e61373b565b5b828204905092915050565b600061353582613622565b915061354083613622565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156135795761357861370c565b5b828202905092915050565b600061358f82613622565b915061359a83613622565b9250828210156135ad576135ac61370c565b5b828203905092915050565b60006135c382613602565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006136378261363e565b9050919050565b600061364982613602565b9050919050565b82818337600083830152505050565b60005b8381101561367d578082015181840152602081019050613662565b8381111561368c576000848401525b50505050565b61369b82613799565b810181811067ffffffffffffffff821117156136ba576136b961376a565b5b80604052505050565b60006136ce82613622565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156137015761370061370c565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f486f70725374616b653a2043616e6e6f7420536166655472616e73666572467260008201527f6f6d20746f6b656e73206f74686572207468616e20486f7072426f6f73742e00602082015250565b7f486f70725374616b653a204c656e67746820646f6573206e6f74206d61746368600082015250565b7f486f70725374616b653a2050726f6772616d20656e6465642c2063616e6e6f7460008201527f2072656465656d20626f6f7374732e0000000000000000000000000000000000602082015250565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b7f486f70725374616b653a2053656e646572206d757374206265207778484f505260008201527f20746f6b656e0000000000000000000000000000000000000000000000000000602082015250565b7f486f70725374616b653a204d7573742062652073656e64696e6720746f6b656e60008201527f7320746f20486f70725374616b6520636f6e7472616374000000000000000000602082015250565b7f486f70725374616b653a204f6e6c7920616363657074206f776e657220746f2060008201527f70726f7669646520726577617264730000000000000000000000000000000000602082015250565b7f486f70725374616b653a2050726f6772616d20656e6465642c2063616e6e6f7460008201527f207374616b6520616e796d6f72652e0000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f486f70725374616b653a2043616e6e6f742072656465656d20616e206578706960008201527f72656420626f6f73742e00000000000000000000000000000000000000000000602082015250565b7f486f70725374616b653a204e6f7468696e6720746f20636c61696d0000000000600082015250565b7f486f70725374616b653a2050726f6772616d206973206f6e676f696e672c206360008201527f616e6e6f7420756e6c6f636b207374616b652e00000000000000000000000000602082015250565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b7f486f70725374616b653a20496e73756666696369656e7420726577617264207060008201527f6f6f6c2e00000000000000000000000000000000000000000000000000000000602082015250565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b7f486f70725374616b653a204f6e6c7920616363657074204c4f434b5f544f4b4560008201527f4e20696e207374616b696e670000000000000000000000000000000000000000602082015250565b613c83816135b8565b8114613c8e57600080fd5b50565b613c9a816135ca565b8114613ca557600080fd5b50565b613cb181613622565b8114613cbc57600080fd5b5056fea26469706673582212203ba240b55b674a9febadd6fd666a05fbbab6c4b2082592c886416ed2c2b0152164736f6c63430008040033