creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    /**\n     * @dev Muldiv operation overflow.\n     */\n    error MathOverflowedMulDiv();\n\n    enum Rounding {\n        Floor, // Toward negative infinity\n        Ceil, // Toward positive infinity\n        Trunc, // Toward zero\n        Expand // Away from zero\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n            // benefit is lost if 'b' is also tested.\n            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n            if (a == 0) return (true, 0);\n            uint256 c = a * b;\n            if (c / a != b) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds towards infinity instead\n     * of rounding towards zero.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (b == 0) {\n            // Guarantee the same behavior as in a regular Solidity division.\n            return a / b;\n        }\n\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n     * denominator == 0.\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n     * Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0 = x * y; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            if (denominator <= prod1) {\n                revert MathOverflowedMulDiv();\n            }\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n            uint256 twos = denominator & (0 - denominator);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n            // works in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n     * towards zero.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n     */\n    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n        return uint8(rounding) % 2 == 1;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/math/SignedMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Strings.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)\n\npragma solidity ^0.8.20;\n\nimport {Math} from \"./math/Math.sol\";\nimport {SignedMath} from \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant HEX_DIGITS = \"0123456789abcdef\";\n    uint8 private constant ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev The `value` string doesn't fit in the specified `length`.\n     */\n    error StringsInsufficientHexLength(uint256 value, uint256 length);\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toStringSigned(int256 value) internal pure returns (string memory) {\n        return string.concat(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value)));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        uint256 localValue = value;\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = HEX_DIGITS[localValue & 0xf];\n            localValue >>= 4;\n        }\n        if (localValue != 0) {\n            revert StringsInsufficientHexLength(value, length);\n        }\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal\n     * representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"
      },
      "contracts/interface/IVesting.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.16;\n\ninterface IVesting{\n    function totalLockedAmount(string memory user) external view returns(uint256);\n    function lock(string memory user, uint256 amount, uint256 unlockTimestamp) external;\n}"
      },
      "contracts/Staking.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.16;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"./interface/IVesting.sol\";\n\n// Stake DLICom tokens and earn USDC tokens\ncontract Staking  is Ownable {\n    address public immutable USDCToken;\n    address public immutable DLIComToken;\n    uint256[] public lockingPeriod;\n    uint256[] public poolMultiplier;\n    \n    mapping (string => StakingInfo[]) public userToStakingInfo;\n    mapping (string => uint256) public totalStakedAmountOfUser;\n    string[] public stakers;\n    mapping (uint256 => uint256) public hourlyUSDCReward;\n    uint256[] public changingHourOfUSDCReward;\n    uint256 public poolMultiplierDivider;\n    uint256 public totalStakedAmount = 0;\n    mapping (uint256 => uint256) hourlyTotalStakedAmount;\n    uint256 public totalUSDCAmount = 0;\n    uint256 public immutable deployTimestamp;\n    mapping (uint256 => uint256) public hourToTotalEffectMap;\n    mapping (uint256 => uint256) public hourUnstakingTotalEffectMap;\n    uint256 public lastChangingHour = 0;\n    uint256[] public changingHourOfTotalEffect;\n    uint256 firstStakeHour = 0;\n    uint256 firstDepositHour = 0;\n    uint256 lastTotalUSDCAmount = 0;\n    uint256 lastClaimHour = 0;\n    bool public marketLaunch = false;\n    address public vestingContract;\n\n    struct StakingInfo {\n        string stakerName;\n        uint256 stakingTime;\n        uint256 lastClaimedTime;\n        uint256 stakingPeriod;\n        uint256 stakedAmount;\n        uint256 unstakedAmount;\n        uint256 unstakedTime;\n        bool isClaimed;\n        uint256 lockingPeriodIndex;\n    }\n\n    struct claimInfo {\n        uint256 claimTime;\n        uint256 claimAmount;\n        uint256 totalStakedAmount;\n        uint256 hourlyUSDCReward;\n    }\n    \n    event Deposit(uint256 amount);\n    event Prestake(string indexed user, uint256 amount);\n    event SetWalletAddress(address indexed user, address walletAddress);\n    event Stake(address indexed user, uint256 amount);\n    event Unstake(address indexed user, uint256 amount);\n    event Claim(address indexed user, uint256 amount);\n    event CheckStakeEligibility(address user, uint256 amount, uint256 walletBalance, uint256 vestingBalance, uint256 stakingBalance, uint256 totalBalance);\n\n    constructor(address _USDCToken, address _DLIComToken, address _vestingContract, uint256[] memory _lockingPeriod, uint256[] memory _poolMultiplier, uint256 _poolMultiplierDivider) Ownable(msg.sender) {\n        require(_USDCToken != address(0), \"Staking::constructor:: Invalid USDC token address\");\n        require(_DLIComToken != address(0), \"Staking::constructor:: Invalid DLICom token address\");\n        require(_lockingPeriod.length == _poolMultiplier.length, \"Staking::constructor:: Invalid input length\");\n        require(_vestingContract != address(0), \"Staking::constructor:: Invalid vesting contract address\");\n        \n        USDCToken = _USDCToken;\n        DLIComToken = _DLIComToken;\n        \n        lockingPeriod = _lockingPeriod; \n        poolMultiplier = _poolMultiplier;\n        poolMultiplierDivider = _poolMultiplierDivider;\n        deployTimestamp = block.timestamp;\n        vestingContract = _vestingContract;\n    }\n\n    modifier onlyVestingContract {\n        require(msg.sender == vestingContract, \"Staking::onlyVestingContract:: Invalid caller\");\n        _;\n    }\n\n    modifier checkStakeEligibility(address user, uint256 amount) {\n        uint256 walletBalance = IERC20(DLIComToken).balanceOf(user);\n        uint256 vestingBalance = IVesting(vestingContract).totalLockedAmount(Strings.toHexString(user));\n        uint256 stakingBalance = totalStakedAmountOfUser[Strings.toHexString(user)];\n        uint256 totalBalance = walletBalance + vestingBalance + stakingBalance;\n        emit CheckStakeEligibility(user, amount, walletBalance, vestingBalance, stakingBalance, totalBalance);\n        require( marketLaunch ||(totalBalance >= 10000 * 10**18), \"Staking::stake:: Insufficient balance\");\n        _;\n    }\n\n    function turnOnMarketLaunch() public onlyOwner {\n        require(!marketLaunch, \"Staking::turnOnMarketLaunch:: Market already launched\");\n        marketLaunch = true;\n    }\n\n    function deposit(uint256 amount) public onlyOwner {\n        require(amount > 0, \"Staking::deposit:: Invalid amount\");\n        \n        bool success = IERC20(USDCToken).transferFrom(msg.sender, address(this), amount);\n        require(success, \"Staking::deposit:: Transfer failed\");\n\n        uint256 lastUSDCChangeHour = changingHourOfUSDCReward.length > 0 ? changingHourOfUSDCReward[changingHourOfUSDCReward.length - 1] : 0;\n        uint256 hour = block.timestamp/(60 * 60);\n        \n        uint256 lastHourlyUSDCRewardAmount = lastUSDCChangeHour == 0 ? 0 : hourlyUSDCReward[lastUSDCChangeHour];\n        uint256 leftUSDCAmount = lastTotalUSDCAmount - ((hour - lastUSDCChangeHour)*lastHourlyUSDCRewardAmount);\n\n        lastTotalUSDCAmount = leftUSDCAmount + amount;\n\n        totalUSDCAmount += amount;\n\n        hourlyUSDCReward[hour] = lastTotalUSDCAmount / 1500;\n        changingHourOfUSDCReward.push(hour);\n        lastClaimHour = hour + 1500;\n\n        if(firstDepositHour == 0){\n            firstDepositHour = hour;\n        }\n\n        emit Deposit(amount);\n    }\n\n    function vestingToStake(address user, uint256 amount, uint256 lockingPeriodIndex) public onlyVestingContract checkStakeEligibility(user,amount) {\n        require(amount > 0, \"Staking::vestingToStake:: Invalid amount\");\n        require(marketLaunch || (lockingPeriodIndex >= 5 && lockingPeriodIndex <= 7), \"Staking::vestingToStake:: Invalid locking period index\");\n\n        bool success = IERC20(DLIComToken).transferFrom(msg.sender, address(this), amount);\n        require(success, \"Staking::vestingToStake:: Transfer failed\");\n\n        string memory walletAddressStr = Strings.toHexString(user);\n        if(userToStakingInfo[walletAddressStr].length == 0 ){\n            stakers.push(walletAddressStr);\n        }\n\n        if(lastChangingHour == 0){\n            lastChangingHour = deployTimestamp/(60 * 60) -1;\n        }\n\n        userToStakingInfo[walletAddressStr].push(StakingInfo({\n            stakerName: walletAddressStr,\n            stakingTime: block.timestamp,\n            lastClaimedTime: block.timestamp,\n            stakingPeriod:lockingPeriod[lockingPeriodIndex],\n            stakedAmount: amount,\n            unstakedAmount: 0,\n            unstakedTime: 0,\n            isClaimed: false,\n            lockingPeriodIndex: lockingPeriodIndex\n            }));\n        \n        totalStakedAmountOfUser[walletAddressStr] += amount;\n\n        uint256 hour = block.timestamp/(60 * 60);\n        hourToTotalEffectMap[hour] = hourToTotalEffectMap[lastChangingHour] + ((amount * poolMultiplier[lockingPeriodIndex])/poolMultiplierDivider) - hourUnstakingTotalEffectMap[hour];\n        if(hour > lastChangingHour){\n            for(uint256 i = lastChangingHour + 1; i < hour; i++){\n                if(hourUnstakingTotalEffectMap[i]!=0){\n                    hourToTotalEffectMap[i] = hourToTotalEffectMap[lastChangingHour] - hourUnstakingTotalEffectMap[i];\n                    changingHourOfTotalEffect.push(i);\n                }\n            }\n        }\n        if(hour != lastChangingHour){\n            changingHourOfTotalEffect.push(hour);\n        }\n        lastChangingHour = hour;\n\n        if(lockingPeriodIndex != 0){\n            uint256 unstakeHour = (block.timestamp + lockingPeriod[lockingPeriodIndex])/(60 * 60);\n            hourUnstakingTotalEffectMap[unstakeHour] += ((amount * poolMultiplier[lockingPeriodIndex])/poolMultiplierDivider);\n        }\n\n        if(firstStakeHour == 0){\n            firstStakeHour = hour;\n        }\n\n        totalStakedAmount += amount;\n        hourlyTotalStakedAmount[hour] = totalStakedAmount;\n\n        emit Stake(msg.sender, amount);\n    }\n\n    function stake(uint256 amount, uint256 lockingPeriodIndex) public checkStakeEligibility(msg.sender,amount) {\n        require(amount > 0, \"Staking::stake:: Invalid amount\");\n        require(marketLaunch || (lockingPeriodIndex >= 5 && lockingPeriodIndex <= 7), \"Staking::stake:: Invalid locking period index\");\n\n        bool success = IERC20(DLIComToken).transferFrom(msg.sender, address(this), amount);\n        require(success, \"Staking::stake:: Transfer failed\");\n\n        string memory walletAddressStr = Strings.toHexString(msg.sender);\n        if(userToStakingInfo[walletAddressStr].length == 0 ){\n            stakers.push(walletAddressStr);\n        }\n\n        if(lastChangingHour == 0){\n            lastChangingHour = deployTimestamp/(60 * 60) -1;\n        }\n\n        userToStakingInfo[walletAddressStr].push(StakingInfo({\n            stakerName: walletAddressStr,\n            stakingTime: block.timestamp,\n            lastClaimedTime: block.timestamp,\n            stakingPeriod:lockingPeriod[lockingPeriodIndex],\n            stakedAmount: amount,\n            unstakedAmount: 0,\n            unstakedTime: 0,\n            isClaimed: false,\n            lockingPeriodIndex: lockingPeriodIndex\n            }));\n        \n        totalStakedAmountOfUser[walletAddressStr] += amount;\n\n        uint256 hour = block.timestamp/(60 * 60);\n        hourToTotalEffectMap[hour] = hourToTotalEffectMap[lastChangingHour] + ((amount * poolMultiplier[lockingPeriodIndex])/poolMultiplierDivider) - hourUnstakingTotalEffectMap[hour];\n        if(hour > lastChangingHour){\n            for(uint256 i = lastChangingHour + 1; i < hour; i++){\n                if(hourUnstakingTotalEffectMap[i]!=0){\n                    hourToTotalEffectMap[i] = hourToTotalEffectMap[lastChangingHour] - hourUnstakingTotalEffectMap[i];\n                    changingHourOfTotalEffect.push(i);\n                }\n            }\n        }\n        if(hour != lastChangingHour){\n            changingHourOfTotalEffect.push(hour);\n        }\n        lastChangingHour = hour;\n\n        if(lockingPeriodIndex != 0){\n            uint256 unstakeHour = (block.timestamp + lockingPeriod[lockingPeriodIndex])/(60 * 60);\n            hourUnstakingTotalEffectMap[unstakeHour] += ((amount * poolMultiplier[lockingPeriodIndex])/poolMultiplierDivider);\n        }\n\n        if(firstStakeHour == 0){\n            firstStakeHour = hour;\n        }\n\n        totalStakedAmount += amount;\n        hourlyTotalStakedAmount[hour] = totalStakedAmount;\n\n        emit Stake(msg.sender, amount);\n    }\n\n    function unstake(uint256 index) public {\n        StakingInfo[] storage stakingInfo = userToStakingInfo[Strings.toHexString(msg.sender)];\n        require(index < stakingInfo.length, \"Staking::unstake:: Invalid amount\");\n        require(stakingInfo[index].unstakedAmount == 0, \"Staking::unstake:: Already unstaked\");\n        require(stakingInfo[index].stakingTime + stakingInfo[index].stakingPeriod < block.timestamp, \"Staking::unstake:: Staking period not completed\");\n\n        stakingInfo[index].unstakedAmount = stakingInfo[index].stakedAmount;\n        stakingInfo[index].unstakedTime = block.timestamp;\n\n        bool success = IERC20(DLIComToken).transfer(msg.sender, stakingInfo[index].unstakedAmount);\n        require(success, \"Staking::unstake:: Transfer failed\");\n\n        totalStakedAmountOfUser[Strings.toHexString(msg.sender)] -= stakingInfo[index].unstakedAmount;\n\n        totalStakedAmount -= stakingInfo[index].unstakedAmount;\n        uint256 hour = block.timestamp/(60 * 60);\n        hourlyTotalStakedAmount[hour] = totalStakedAmount;\n\n        if(stakingInfo[index].lockingPeriodIndex == 0){\n            hourToTotalEffectMap[hour] = hourToTotalEffectMap[lastChangingHour] - ((stakingInfo[index].stakedAmount * poolMultiplier[stakingInfo[index].lockingPeriodIndex])/poolMultiplierDivider);\n            changingHourOfTotalEffect.push(hour);\n            lastChangingHour = hour;\n        }\n\n        emit Unstake(msg.sender, stakingInfo[index].unstakedAmount);\n    }\n\n    function claim() public {\n        uint256 currentTimestamp = block.timestamp;\n        uint256 claimableAmount = getClaimAmount(msg.sender, currentTimestamp);\n        StakingInfo[] storage stakingInfo = userToStakingInfo[Strings.toHexString(msg.sender)];\n        require(stakingInfo.length > 0, \"Staking::claim:: No staking info\");\n\n        for (uint i = 0; i < stakingInfo.length; i++) {\n            if((stakingInfo[i].lockingPeriodIndex == 0 && stakingInfo[i].unstakedTime < currentTimestamp ) || (!stakingInfo[i].isClaimed && stakingInfo[i].lastClaimedTime < stakingInfo[i].stakingTime + stakingInfo[i].stakingPeriod)){\n                stakingInfo[i].lastClaimedTime = currentTimestamp;\n            }\n            else{\n                stakingInfo[i].isClaimed = true;\n            }\n        }\n\n        require(claimableAmount > 0, \"Staking::claim:: No claimable amount\");\n\n        bool success = IERC20(USDCToken).transfer(msg.sender, claimableAmount);\n        require(success, \"Staking::claim:: Transfer failed\");\n\n        totalUSDCAmount -= claimableAmount;\n\n        emit Claim(msg.sender, claimableAmount);\n    }\n\n    function getClaimAmount(address staker, uint256 timestamp) public view returns (uint256) {\n        uint256 newClaimableAmount = 0;\n        StakingInfo[] storage stakingInfo = userToStakingInfo[Strings.toHexString(staker)];\n        require(stakingInfo.length > 0, \"Staking::getClaimAmount:: No staking info\");\n\n        if(changingHourOfUSDCReward.length == 0){\n            return newClaimableAmount;\n        }\n\n        for (uint256 k = 0; k < stakingInfo.length; k++) {\n            if(stakingInfo[k].isClaimed){\n                continue;\n            }\n            uint256 startingHour = stakingInfo[k].lastClaimedTime/(60 * 60);\n            \n            uint256 endStakingHour = stakingInfo[k].unstakedTime > stakingInfo[k].stakingTime ? (stakingInfo[k].unstakedTime)/(60 * 60): (stakingInfo[k].stakingTime + stakingInfo[k].stakingPeriod)/(60 * 60);\n            uint256 timestampHour = timestamp/(60 * 60);\n            uint256 endingHour = (stakingInfo[k].lockingPeriodIndex == 0 && stakingInfo[k].unstakedTime < stakingInfo[k].stakingTime)  ? timestampHour : (endStakingHour < timestampHour) ? endStakingHour : timestampHour;\n\n            uint256 curHourlyUSDCReward = 0;\n            uint256 curTotalEffect = 0;\n            uint256 nextTotalEffectChangingHour = 0;\n            uint256 nextUSDCRewardChangingHour = 0;\n            uint256 curTotalUnstakeEffect = 0;\n\n            (curHourlyUSDCReward, nextUSDCRewardChangingHour) = binarySearchUSDCReward(startingHour);\n            if(startingHour < changingHourOfUSDCReward[nextUSDCRewardChangingHour - 1]){\n                startingHour = changingHourOfUSDCReward[nextUSDCRewardChangingHour - 1];\n                curHourlyUSDCReward = hourlyUSDCReward[startingHour];\n            }\n\n            (curTotalEffect, nextTotalEffectChangingHour) = binarySearchTotalEffect(startingHour);\n\n\n            if(endingHour > lastClaimHour){\n                endingHour = lastClaimHour;\n            }\n\n            for(uint256 i = startingHour; i < endingHour;){\n                if(hourlyUSDCReward[i] != 0){\n                    curHourlyUSDCReward = hourlyUSDCReward[i];\n                }\n                if(hourToTotalEffectMap[i] != 0){\n                    curTotalEffect = hourToTotalEffectMap[i];\n                }\n                if(hourUnstakingTotalEffectMap[i] != 0 ){\n                    curTotalUnstakeEffect += hourUnstakingTotalEffectMap[i];\n                }\n\n                uint256 nextPointer;\n                if(nextTotalEffectChangingHour >= changingHourOfTotalEffect.length && nextUSDCRewardChangingHour >= changingHourOfUSDCReward.length){\n                    nextPointer = endingHour;\n                }\n                else if(nextTotalEffectChangingHour >= changingHourOfTotalEffect.length){\n                    nextPointer = changingHourOfUSDCReward[nextUSDCRewardChangingHour];\n                }\n                else if(nextUSDCRewardChangingHour >= changingHourOfUSDCReward.length){\n                    nextPointer = changingHourOfTotalEffect[nextTotalEffectChangingHour];\n                }\n                else{\n                    nextPointer = Math.min(changingHourOfTotalEffect[nextTotalEffectChangingHour], changingHourOfUSDCReward[nextUSDCRewardChangingHour]);\n                }\n\n                if(curTotalEffect - curTotalUnstakeEffect <= 0){\n                    i = nextPointer;\n                    nextTotalEffectChangingHour++;\n                    nextUSDCRewardChangingHour++;\n                    continue;\n                }\n\n                if(nextPointer > i ){\n                    newClaimableAmount += ((nextPointer - i) * (stakingInfo[k].stakedAmount * poolMultiplier[stakingInfo[k].lockingPeriodIndex] * curHourlyUSDCReward) / ((curTotalEffect - curTotalUnstakeEffect) * poolMultiplierDivider));\n                    i = nextPointer;\n                    nextTotalEffectChangingHour++;\n                    nextUSDCRewardChangingHour++;\n                }\n                else{\n                    newClaimableAmount += ((endingHour - i) * (stakingInfo[k].stakedAmount * poolMultiplier[stakingInfo[k].lockingPeriodIndex] * curHourlyUSDCReward) / ((curTotalEffect - curTotalUnstakeEffect) * poolMultiplierDivider));\n                    i = endingHour;\n                }\n            }\n        }\n        return newClaimableAmount;\n    }\n\n    // Binary search to find the total effect of the stakers for a given hour\n    // Returns the total effect and the next index of the hour in the changingHourOfTotalEffect array\n    function binarySearchTotalEffect(uint256 startingHour) public view returns (uint256, uint256) {\n        uint256 low = 0;\n        uint256 high = changingHourOfTotalEffect.length - 1;\n        uint256 mid = 0;\n        \n        while(low <= high){\n            mid = (low + high) / 2;\n            if(mid == 0 && changingHourOfTotalEffect[mid] > startingHour){\n                return (0,1);\n            }\n            else if(mid == 0 && changingHourOfTotalEffect[mid]<= startingHour){\n                if(mid + 1 < changingHourOfTotalEffect.length && changingHourOfTotalEffect[mid + 1] <= startingHour)\n                    return (hourToTotalEffectMap[changingHourOfTotalEffect[mid+1]], mid+2);\n                else\n                    return (hourToTotalEffectMap[changingHourOfTotalEffect[mid]], mid+1);\n            }\n            if(changingHourOfTotalEffect[mid] == startingHour){\n                if(mid + 1 < changingHourOfTotalEffect.length)\n                    return (hourToTotalEffectMap[changingHourOfTotalEffect[mid]], mid+1);\n                else\n                    return (hourToTotalEffectMap[changingHourOfTotalEffect[mid]], mid+1);\n            }\n            else if(mid > 0 && changingHourOfTotalEffect[mid] > startingHour && changingHourOfTotalEffect[mid - 1] < startingHour){\n                return (hourToTotalEffectMap[changingHourOfTotalEffect[mid-1]], mid);\n            }\n            else if(changingHourOfTotalEffect[mid] < startingHour){\n                low = mid + 1;\n            }\n            else{\n                high = mid - 1;\n            }\n        }\n        return (hourToTotalEffectMap[changingHourOfTotalEffect[mid]],mid+1);\n    }\n\n    function binarySearchUSDCReward(uint256 hour) public view returns (uint256 , uint256){\n        uint256 low = 0;\n        uint256 high = changingHourOfUSDCReward.length-1;\n        uint256 mid = 0;\n        \n        while(low <= high){\n            mid = (low + high) / 2;\n            if(mid == 0 && changingHourOfUSDCReward[mid] > hour){\n                return (0,1);\n            }\n            else if(mid == 0 && changingHourOfUSDCReward[mid]<= hour){\n                if(mid + 1 < changingHourOfUSDCReward.length && changingHourOfUSDCReward[mid + 1] <= hour)\n                    return (hourlyUSDCReward[changingHourOfUSDCReward[mid+1]], mid+2);\n                else\n                    return (hourlyUSDCReward[changingHourOfUSDCReward[mid]], mid+1);\n            }\n            if(changingHourOfUSDCReward[mid] == hour){\n                if(mid + 1 < changingHourOfUSDCReward.length)\n                    return (hourlyUSDCReward[changingHourOfUSDCReward[mid]], mid+1);\n                else\n                    return (hourlyUSDCReward[changingHourOfUSDCReward[mid]], mid+1);\n            }\n            else if(mid > 0 && changingHourOfUSDCReward[mid] > hour && changingHourOfUSDCReward[mid - 1] < hour){\n                return (hourlyUSDCReward[changingHourOfUSDCReward[mid-1]], mid);\n            }\n            else if(changingHourOfUSDCReward[mid] < hour){\n                low = mid + 1;\n            }\n            else{\n                high = mid - 1;\n            }\n        }\n        return (hourlyUSDCReward[changingHourOfUSDCReward[mid]],mid+1);\n    }\n\n    function binarySearchTotalStakedAmount(uint256 hour) public view returns (uint256 , uint256){\n        uint256 low = 0;\n        uint256 high = changingHourOfTotalEffect.length-1;\n        uint256 mid = 0;\n        \n        while(low <= high){\n            mid = (low + high) / 2;\n            if(mid == 0 && changingHourOfTotalEffect[mid] > hour){\n                return (0,1);\n            }\n            else if(mid == 0 && changingHourOfTotalEffect[mid]<= hour){\n                if(mid + 1 < changingHourOfTotalEffect.length && changingHourOfTotalEffect[mid + 1] <= hour)\n                    return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid+1]],mid+2);\n                else\n                    return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid]],mid+1);\n            }\n            if(changingHourOfTotalEffect[mid] == hour){\n                if(mid + 1 < changingHourOfTotalEffect.length)\n                    return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid]], mid+1);\n                else\n                    return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid]], mid+1);\n            }\n            else if(mid > 0 && changingHourOfTotalEffect[mid] > hour && changingHourOfTotalEffect[mid - 1] < hour){\n                return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid-1]], mid);\n            }\n            else if(changingHourOfTotalEffect[mid] < hour){\n                low = mid + 1;\n            }\n            else{\n                high = mid - 1;\n            }\n        }\n        return (hourlyTotalStakedAmount[changingHourOfTotalEffect[mid]],mid+1);\n    }\n\n    function getClaimAmountHistory(address staker, uint256 startTimestamp, uint256 endTimestamp) public view returns (claimInfo[] memory) {\n        require(startTimestamp < endTimestamp, \"Staking::getClaimAmountHistory:: Invalid timestamp\");\n        StakingInfo[] storage stakingInfo = userToStakingInfo[Strings.toHexString(staker)];\n        uint256 startingHour = startTimestamp/(60 * 60);\n        uint256 endingHour = endTimestamp/(60 * 60);\n        require(stakingInfo.length > 0, \"Staking::getClaimAmountHistory:: No staking info\");\n\n        claimInfo[] memory claimInfoArray = new claimInfo[](endingHour - startingHour + 1);\n        if(changingHourOfUSDCReward.length == 0){\n            return claimInfoArray;\n        }\n        (uint256 curHourlyUSDCReward, ) = binarySearchUSDCReward(startingHour);\n        (uint256 curTotalEffect, ) = binarySearchTotalEffect(startingHour);\n        (uint256 curTotalStakedAmount, ) = binarySearchTotalStakedAmount(startingHour);\n        uint256 curTotalUnstakeEffect = 0;\n\n        for(uint256 i = startingHour; i < endingHour; i++){\n            uint256 claimableAmount = 0;\n            if(hourlyUSDCReward[i] != 0){\n                curHourlyUSDCReward = hourlyUSDCReward[i];\n            }\n            if(hourToTotalEffectMap[i] != 0){\n                curTotalEffect = hourToTotalEffectMap[i];\n            }\n            if(hourlyTotalStakedAmount[i]!=0){\n                curTotalStakedAmount = hourlyTotalStakedAmount[i];\n            }\n            if(i > lastChangingHour && hourlyTotalStakedAmount[i] != 0){\n                curTotalUnstakeEffect += hourlyTotalStakedAmount[i];\n            }\n\n            if(curHourlyUSDCReward == 0 || curTotalEffect == 0){\n                continue;\n            }\n            if(i >= lastClaimHour){\n                claimInfoArray[i - startingHour] = claimInfo({\n                    claimTime: (i + 1) * (60 * 60),\n                    claimAmount: 0,\n                    totalStakedAmount: curTotalStakedAmount,\n                    hourlyUSDCReward: curHourlyUSDCReward\n                });\n                continue;\n            }\n\n            for (uint k = 0; k < stakingInfo.length; k++) {\n                if(stakingInfo[k].isClaimed){\n                    continue;\n                }\n                if( stakingInfo[k].unstakedTime > 0 && stakingInfo[k].unstakedTime/(60*60) <= i){\n                    continue;\n                }\n                if(curTotalEffect != 0  && curTotalEffect-curTotalUnstakeEffect !=0 && (stakingInfo[k].stakingTime/(60*60) <= i && (stakingInfo[k].stakingTime + stakingInfo[k].stakingPeriod)/(60*60) > i)){\n                    claimableAmount += (stakingInfo[k].stakedAmount * poolMultiplier[stakingInfo[k].lockingPeriodIndex] * curHourlyUSDCReward) / ((curTotalEffect-curTotalUnstakeEffect) * poolMultiplierDivider);\n                }\n                else if(curTotalEffect != 0 && curTotalEffect-curTotalUnstakeEffect !=0 && stakingInfo[k].stakingTime/(60*60) <= i &&  stakingInfo[k].lockingPeriodIndex == 0 ){\n                    claimableAmount += (stakingInfo[k].stakedAmount * poolMultiplier[stakingInfo[k].lockingPeriodIndex] * curHourlyUSDCReward) / ((curTotalEffect-curTotalUnstakeEffect) * poolMultiplierDivider);\n                }\n            }\n            claimInfoArray[i - startingHour] = claimInfo({\n                claimTime: (i + 1) * (60 * 60),\n                claimAmount: claimableAmount,\n                totalStakedAmount: curTotalStakedAmount,\n                hourlyUSDCReward: curHourlyUSDCReward\n            });\n        }\n        return claimInfoArray;\n    }\n\n    function getStakerInfo(address staker) public view returns (StakingInfo[] memory) {\n        return userToStakingInfo[Strings.toHexString(staker)];\n    }\n}"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 0
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}