creation_sourcecode: {"Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn\u0027t rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length \u003e 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity\u0027s `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n        require(isContract(target), \"Address: call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verifies that a low level call was successful, and revert if it wasn\u0027t, either by bubbling the\n     * revert reason using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length \u003e 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}"},"EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping(bytes32 =\u003e uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value\u0027s index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as \u0027swap and pop\u0027).\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (lastIndex != toDeleteIndex) {\n                bytes32 lastvalue = set._values[lastIndex];\n\n                // Move the last value to the index where the value to delete is\n                set._values[toDeleteIndex] = lastvalue;\n                // Update the index for the moved value\n                set._indexes[lastvalue] = valueIndex; // Replace lastvalue\u0027s index to valueIndex\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        return _values(set._inner);\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}"},"FullMath.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.4.0;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n  /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n  /// @param a The multiplicand\n  /// @param b The multiplier\n  /// @param denominator The divisor\n  /// @return result The 256-bit result\n  /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n  function mulDiv(\n    uint256 a,\n    uint256 b,\n    uint256 denominator\n  ) internal pure returns (uint256 result) {\n    // 512-bit multiply [prod1 prod0] = a * b\n    // Compute the product mod 2**256 and mod 2**256 - 1\n    // then use the Chinese Remainder Theorem to reconstruct\n    // the 512 bit result. The result is stored in two 256\n    // variables such that product = prod1 * 2**256 + prod0\n    uint256 prod0; // Least significant 256 bits of the product\n    uint256 prod1; // Most significant 256 bits of the product\n    assembly {\n      let mm := mulmod(a, b, not(0))\n      prod0 := mul(a, b)\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      require(denominator \u003e 0);\n      assembly {\n        result := div(prod0, denominator)\n      }\n      return result;\n    }\n\n    // Make sure the result is less than 2**256.\n    // Also prevents denominator == 0\n    require(denominator \u003e prod1);\n\n    ///////////////////////////////////////////////\n    // 512 by 256 division.\n    ///////////////////////////////////////////////\n\n    // Make division exact by subtracting the remainder from [prod1 prod0]\n    // Compute remainder using mulmod\n    uint256 remainder;\n    assembly {\n      remainder := mulmod(a, b, denominator)\n    }\n    // Subtract 256 bit number from 512 bit number\n    assembly {\n      prod1 := sub(prod1, gt(remainder, prod0))\n      prod0 := sub(prod0, remainder)\n    }\n\n    // Factor powers of two out of denominator\n    // Compute largest power of two divisor of denominator.\n    // Always \u003e= 1.\n    unchecked {\n      uint256 twos = (type(uint256).max - denominator + 1) \u0026 denominator;\n      // Divide denominator by power of two\n      assembly {\n        denominator := div(denominator, twos)\n      }\n\n      // Divide [prod1 prod0] by the factors of two\n      assembly {\n        prod0 := div(prod0, twos)\n      }\n      // Shift in bits from prod1 into prod0. For this we need\n      // to flip `twos` such that it is 2**256 / twos.\n      // If twos is zero, then it becomes one\n      assembly {\n        twos := add(div(sub(0, twos), twos), 1)\n      }\n      prod0 |= prod1 * twos;\n\n      // Invert denominator mod 2**256\n      // Now that denominator is an odd number, it has an inverse\n      // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n      // Compute the inverse by starting with a seed that is correct\n      // correct for four bits. That is, denominator * inv = 1 mod 2**4\n      uint256 inv = (3 * denominator) ^ 2;\n      // Now use Newton-Raphson iteration to improve the precision.\n      // Thanks to Hensel\u0027s lifting lemma, this also works in modular\n      // arithmetic, doubling the correct bits in each step.\n      inv *= 2 - denominator * inv; // inverse mod 2**8\n      inv *= 2 - denominator * inv; // inverse mod 2**16\n      inv *= 2 - denominator * inv; // inverse mod 2**32\n      inv *= 2 - denominator * inv; // inverse mod 2**64\n      inv *= 2 - denominator * inv; // inverse mod 2**128\n      inv *= 2 - denominator * inv; // inverse mod 2**256\n\n      // Because the division is now exact we can divide by multiplying\n      // with the modular inverse of denominator. This will give us the\n      // correct result modulo 2**256. Since the precoditions guarantee\n      // that the outcome is less than 2**256, this is the final result.\n      // We don\u0027t need to compute the high bits of the result and prod1\n      // is no longer required.\n      result = prod0 * inv;\n      return result;\n    }\n  }\n}"},"IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s 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 amount) 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 `amount` as the allowance of `spender` over the caller\u0027s 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\u0027s 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 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller\u0027s\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n\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}"},"IPinkLock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IPinkLock {\n    function lock(\n        address owner,\n        address token,\n        bool isLpToken,\n        uint256 amount,\n        uint256 unlockDate,\n        string memory description\n    ) external returns (uint256 lockId);\n\n    function vestingLock(\n        address owner,\n        address token,\n        bool isLpToken,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) external returns (uint256 lockId);\n\n    function multipleVestingLock(\n        address[] calldata owners,\n        uint256[] calldata amounts,\n        address token,\n        bool isLpToken,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) external returns (uint256[] memory);\n\n    function unlock(uint256 lockId) external;\n\n    function editLock(\n        uint256 lockId,\n        uint256 newAmount,\n        uint256 newUnlockDate\n    ) external;\n}"},"IUniswapV2Factory.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IUniswapV2Factory {\n  event PairCreated(\n    address indexed token0,\n    address indexed token1,\n    address pair,\n    uint256\n  );\n\n  function feeTo() external view returns (address);\n\n  function feeToSetter() external view returns (address);\n\n  function getPair(address tokenA, address tokenB)\n    external\n    view\n    returns (address pair);\n\n  function allPairs(uint256) external view returns (address pair);\n\n  function allPairsLength() external view returns (uint256);\n\n  function createPair(address tokenA, address tokenB)\n    external\n    returns (address pair);\n\n  function setFeeTo(address) external;\n\n  function setFeeToSetter(address) external;\n}"},"IUniswapV2Pair.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IUniswapV2Pair {\n  event Approval(address indexed owner, address indexed spender, uint256 value);\n  event Transfer(address indexed from, address indexed to, uint256 value);\n\n  function name() external pure returns (string memory);\n\n  function symbol() external pure returns (string memory);\n\n  function decimals() external pure returns (uint8);\n\n  function totalSupply() external view returns (uint256);\n\n  function balanceOf(address owner) external view returns (uint256);\n\n  function allowance(address owner, address spender)\n    external\n    view\n    returns (uint256);\n\n  function approve(address spender, uint256 value) external returns (bool);\n\n  function transfer(address to, uint256 value) external returns (bool);\n\n  function transferFrom(\n    address from,\n    address to,\n    uint256 value\n  ) external returns (bool);\n\n  function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n  function PERMIT_TYPEHASH() external pure returns (bytes32);\n\n  function nonces(address owner) external view returns (uint256);\n\n  function permit(\n    address owner,\n    address spender,\n    uint256 value,\n    uint256 deadline,\n    uint8 v,\n    bytes32 r,\n    bytes32 s\n  ) external;\n\n  event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n  event Burn(\n    address indexed sender,\n    uint256 amount0,\n    uint256 amount1,\n    address indexed to\n  );\n  event Swap(\n    address indexed sender,\n    uint256 amount0In,\n    uint256 amount1In,\n    uint256 amount0Out,\n    uint256 amount1Out,\n    address indexed to\n  );\n  event Sync(uint112 reserve0, uint112 reserve1);\n\n  function MINIMUM_LIQUIDITY() external pure returns (uint256);\n\n  function factory() external view returns (address);\n\n  function token0() external view returns (address);\n\n  function token1() external view returns (address);\n\n  function getReserves()\n    external\n    view\n    returns (\n      uint112 reserve0,\n      uint112 reserve1,\n      uint32 blockTimestampLast\n    );\n\n  function price0CumulativeLast() external view returns (uint256);\n\n  function price1CumulativeLast() external view returns (uint256);\n\n  function kLast() external view returns (uint256);\n\n  function mint(address to) external returns (uint256 liquidity);\n\n  function burn(address to) external returns (uint256 amount0, uint256 amount1);\n\n  function swap(\n    uint256 amount0Out,\n    uint256 amount1Out,\n    address to,\n    bytes calldata data\n  ) external;\n\n  function skim(address to) external;\n\n  function sync() external;\n\n  function initialize(address, address) external;\n}"},"IUniswapV2Router02.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IUniswapV2Router01 {\n  function factory() external pure returns (address);\n\n  function WETH() external pure returns (address);\n\n  function addLiquidity(\n    address tokenA,\n    address tokenB,\n    uint256 amountADesired,\n    uint256 amountBDesired,\n    uint256 amountAMin,\n    uint256 amountBMin,\n    address to,\n    uint256 deadline\n  )\n    external\n    returns (\n      uint256 amountA,\n      uint256 amountB,\n      uint256 liquidity\n    );\n\n  function addLiquidityETH(\n    address token,\n    uint256 amountTokenDesired,\n    uint256 amountTokenMin,\n    uint256 amountETHMin,\n    address to,\n    uint256 deadline\n  )\n    external\n    payable\n    returns (\n      uint256 amountToken,\n      uint256 amountETH,\n      uint256 liquidity\n    );\n\n  function removeLiquidity(\n    address tokenA,\n    address tokenB,\n    uint256 liquidity,\n    uint256 amountAMin,\n    uint256 amountBMin,\n    address to,\n    uint256 deadline\n  ) external returns (uint256 amountA, uint256 amountB);\n\n  function removeLiquidityETH(\n    address token,\n    uint256 liquidity,\n    uint256 amountTokenMin,\n    uint256 amountETHMin,\n    address to,\n    uint256 deadline\n  ) external returns (uint256 amountToken, uint256 amountETH);\n\n  function removeLiquidityWithPermit(\n    address tokenA,\n    address tokenB,\n    uint256 liquidity,\n    uint256 amountAMin,\n    uint256 amountBMin,\n    address to,\n    uint256 deadline,\n    bool approveMax,\n    uint8 v,\n    bytes32 r,\n    bytes32 s\n  ) external returns (uint256 amountA, uint256 amountB);\n\n  function removeLiquidityETHWithPermit(\n    address token,\n    uint256 liquidity,\n    uint256 amountTokenMin,\n    uint256 amountETHMin,\n    address to,\n    uint256 deadline,\n    bool approveMax,\n    uint8 v,\n    bytes32 r,\n    bytes32 s\n  ) external returns (uint256 amountToken, uint256 amountETH);\n\n  function swapExactTokensForTokens(\n    uint256 amountIn,\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external returns (uint256[] memory amounts);\n\n  function swapTokensForExactTokens(\n    uint256 amountOut,\n    uint256 amountInMax,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external returns (uint256[] memory amounts);\n\n  function swapExactETHForTokens(\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external payable returns (uint256[] memory amounts);\n\n  function swapTokensForExactETH(\n    uint256 amountOut,\n    uint256 amountInMax,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external returns (uint256[] memory amounts);\n\n  function swapExactTokensForETH(\n    uint256 amountIn,\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external returns (uint256[] memory amounts);\n\n  function swapETHForExactTokens(\n    uint256 amountOut,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external payable returns (uint256[] memory amounts);\n\n  function quote(\n    uint256 amountA,\n    uint256 reserveA,\n    uint256 reserveB\n  ) external pure returns (uint256 amountB);\n\n  function getAmountOut(\n    uint256 amountIn,\n    uint256 reserveIn,\n    uint256 reserveOut\n  ) external pure returns (uint256 amountOut);\n\n  function getAmountIn(\n    uint256 amountOut,\n    uint256 reserveIn,\n    uint256 reserveOut\n  ) external pure returns (uint256 amountIn);\n\n  function getAmountsOut(uint256 amountIn, address[] calldata path)\n    external\n    view\n    returns (uint256[] memory amounts);\n\n  function getAmountsIn(uint256 amountOut, address[] calldata path)\n    external\n    view\n    returns (uint256[] memory amounts);\n}\n\ninterface IUniswapV2Router02 is IUniswapV2Router01 {\n  function removeLiquidityETHSupportingFeeOnTransferTokens(\n    address token,\n    uint256 liquidity,\n    uint256 amountTokenMin,\n    uint256 amountETHMin,\n    address to,\n    uint256 deadline\n  ) external returns (uint256 amountETH);\n\n  function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\n    address token,\n    uint256 liquidity,\n    uint256 amountTokenMin,\n    uint256 amountETHMin,\n    address to,\n    uint256 deadline,\n    bool approveMax,\n    uint8 v,\n    bytes32 r,\n    bytes32 s\n  ) external returns (uint256 amountETH);\n\n  function swapExactTokensForTokensSupportingFeeOnTransferTokens(\n    uint256 amountIn,\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external;\n\n  function swapExactETHForTokensSupportingFeeOnTransferTokens(\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external payable;\n\n  function swapExactTokensForETHSupportingFeeOnTransferTokens(\n    uint256 amountIn,\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external;\n}"},"PinkLock02.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport \"./Address.sol\";\nimport \"./IERC20.sol\";\nimport \"./SafeERC20.sol\";\nimport \"./EnumerableSet.sol\";\n\nimport \"./IPinkLock.sol\";\nimport \"./IUniswapV2Router02.sol\";\nimport \"./IUniswapV2Pair.sol\";\nimport \"./IUniswapV2Factory.sol\";\nimport \"./FullMath.sol\";\n\ncontract PinkLock02 is IPinkLock {\n    using Address for address payable;\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using EnumerableSet for EnumerableSet.UintSet;\n    using SafeERC20 for IERC20;\n\n    struct Lock {\n        uint256 id;\n        address token;\n        address owner;\n        uint256 amount;\n        uint256 lockDate;\n        uint256 tgeDate; // TGE date for vesting locks, unlock date for normal locks\n        uint256 tgeBps; // In bips. Is 0 for normal locks\n        uint256 cycle; // Is 0 for normal locks\n        uint256 cycleBps; // In bips. Is 0 for normal locks\n        uint256 unlockedAmount;\n        string description;\n    }\n\n    struct CumulativeLockInfo {\n        address token;\n        address factory;\n        uint256 amount;\n    }\n\n    // ID padding from PinkLock v1, as there is a lack of a pausing mechanism\n    // as of now the lastest id from v1 is about 22K, so this is probably a safe padding value.\n    uint256 private constant ID_PADDING = 1_000_000;\n\n    Lock[] private _locks;\n    mapping(address =\u003e EnumerableSet.UintSet) private _userLpLockIds;\n    mapping(address =\u003e EnumerableSet.UintSet) private _userNormalLockIds;\n\n    EnumerableSet.AddressSet private _lpLockedTokens;\n    EnumerableSet.AddressSet private _normalLockedTokens;\n    mapping(address =\u003e CumulativeLockInfo) public cumulativeLockInfo;\n    mapping(address =\u003e EnumerableSet.UintSet) private _tokenToLockIds;\n\n    // event LockAdded(\n    //     uint256 indexed id,\n    //     address token,\n    //     address owner,\n    //     uint256 amount,\n    //     uint256 lockDate,\n    //     uint256 tgeDate,\n    //     uint256 tgeBps,\n    //     uint256 cycle,\n    //     uint256 cycleBps,\n    //     string description\n    // );\n\n    event LockAdded(\n        uint256 indexed id,\n        address token,\n        address owner,\n        bool bIsLpToken,\n        uint256[] data,\n        string description\n    );\n\n    // event LockAdded(\n    //     uint256 indexed id,\n    //     address token,\n    //     address owner,\n    //     uint256 amount,\n    //     uint256 unlockDate\n    // );\n\n    event LockUpdated(\n        uint256 indexed id,\n        address token,\n        address owner,\n        uint256 newAmount,\n        uint256 newUnlockDate\n    );\n    event LockRemoved(\n        uint256 indexed id,\n        address token,\n        address owner,\n        uint256 amount,\n        uint256 unlockedAt\n    );\n    event LockVested(\n        uint256 indexed id,\n        address token,\n        address owner,\n        uint256 amount,\n        uint256 remaining,\n        uint256 timestamp\n    );\n    event LockDescriptionChanged(uint256 lockId);\n    event LockOwnerChanged(uint256 lockId, address owner, address newOwner);\n\n    modifier validLock(uint256 lockId) {\n        _getActualIndex(lockId);\n        _;\n    }\n\n    function lock(\n        address owner,\n        address token,\n        bool isLpToken,\n        uint256 amount,\n        uint256 unlockDate,\n        string memory description\n    ) external override returns (uint256 id) {\n        require(token != address(0), \"Invalid token\");\n        require(amount \u003e 0, \"Amount should be greater than 0\");\n        require(\n            unlockDate \u003e block.timestamp,\n            \"Unlock date should be in the future\"\n        );\n        id = _createLock(\n            owner,\n            token,\n            isLpToken,\n            amount,\n            unlockDate,\n            0,\n            0,\n            0,\n            description\n        );\n        _safeTransferFromEnsureExactAmount(\n            token,\n            msg.sender,\n            address(this),\n            amount\n        );\n        uint256[] memory data = new uint256[](6);\n        data[0] = amount;\n        data[1] = block.timestamp;\n        data[2] = unlockDate;\n        data[3] = 0;\n        data[4] = 0;\n        data[5] = 0;\n        emit LockAdded(id, token, owner, isLpToken, data, description);\n        // emit LockAdded(id, token, owner, amount, isLpToken, unlockDate, 0, 0, 0,description);\n        // emit LockAdded(id, token, owner, amount, unlockDate);\n        return id;\n    }\n\n    function vestingLock(\n        address owner,\n        address token,\n        bool isLpToken,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) external override returns (uint256 id) {\n        require(token != address(0), \"Invalid token\");\n        require(amount \u003e 0, \"Amount should be greater than 0\");\n        require(tgeDate \u003e block.timestamp, \"TGE date should be in the future\");\n        require(cycle \u003e 0, \"Invalid cycle\");\n        require(tgeBps \u003e 0 \u0026\u0026 tgeBps \u003c 10_000, \"Invalid bips for TGE\");\n        require(cycleBps \u003e 0 \u0026\u0026 cycleBps \u003c 10_000, \"Invalid bips for cycle\");\n        require(\n            tgeBps + cycleBps \u003c= 10_000,\n            \"Sum of TGE bps and cycle should be less than 10000\"\n        );\n        id = _createLock(\n            owner,\n            token,\n            isLpToken,\n            amount,\n            tgeDate,\n            tgeBps,\n            cycle,\n            cycleBps,\n            description\n        );\n        _safeTransferFromEnsureExactAmount(\n            token,\n            msg.sender,\n            address(this),\n            amount\n        );\n\n        uint256[] memory data = new uint256[](6);\n        data[0] = amount;\n        data[1] = block.timestamp;\n        data[2] = tgeDate;\n        data[3] = tgeBps;\n        data[4] = cycle;\n        data[5] = cycleBps;\n        emit LockAdded(id, token, owner, isLpToken, data, description);\n        // emit LockAdded(id, token, owner, amount, block.timestamp, tgeDate, tgeBps, cycle, cycleBps,description);\n        // emit LockAdded(id, token, owner, amount, tgeDate);\n        return id;\n    }\n\n    function multipleVestingLock(\n        address[] calldata owners,\n        uint256[] calldata amounts,\n        address token,\n        bool isLpToken,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) external override returns (uint256[] memory) {\n        require(token != address(0), \"Invalid token\");\n        require(owners.length == amounts.length, \"Length mismatched\");\n        require(tgeDate \u003e block.timestamp, \"TGE date should be in the future\");\n        require(cycle \u003e 0, \"Invalid cycle\");\n        require(tgeBps \u003e 0 \u0026\u0026 tgeBps \u003c 10_000, \"Invalid bips for TGE\");\n        require(cycleBps \u003e 0 \u0026\u0026 cycleBps \u003c 10_000, \"Invalid bips for cycle\");\n        require(\n            tgeBps + cycleBps \u003c= 10_000,\n            \"Sum of TGE bps and cycle should be less than 10000\"\n        );\n        return\n            _multipleVestingLock(\n                owners,\n                amounts,\n                token,\n                isLpToken,\n                [tgeDate, tgeBps, cycle, cycleBps],\n                description\n            );\n    }\n\n    function _multipleVestingLock(\n        address[] calldata owners,\n        uint256[] calldata amounts,\n        address token,\n        bool isLpToken,\n        uint256[4] memory vestingSettings, // avoid stack too deep\n        string memory description\n    ) internal returns (uint256[] memory) {\n        require(token != address(0), \"Invalid token\");\n        uint256 sumAmount = _sumAmount(amounts);\n        uint256 count = owners.length;\n        uint256[] memory ids = new uint256[](count);\n        for (uint256 i = 0; i \u003c count; i++) {\n            ids[i] = _createLock(\n                owners[i],\n                token,\n                isLpToken,\n                amounts[i],\n                vestingSettings[0], // TGE date\n                vestingSettings[1], // TGE bps\n                vestingSettings[2], // cycle\n                vestingSettings[3], // cycle bps\n                description\n            );\n\n        uint256[] memory data = new uint256[](6);\n        data[0] = amounts[i];\n        data[1] = block.timestamp;\n        data[2] = vestingSettings[0];\n        data[3] = vestingSettings[1];\n        data[4] = vestingSettings[2];\n        data[5] = vestingSettings[3];\n        address owner = owners[i];\n        uint256 id = ids[i];\n        emit LockAdded(id, token, owner, isLpToken, data, description);\n            // emit LockAdded(\n            // ids[i], \n            // token, \n            // owners[i], \n            // amounts[i], \n            // block.timestamp, \n            // vestingSettings[0], \n            // vestingSettings[1], \n            // vestingSettings[2], \n            // vestingSettings[3],\n            // description);\n\n            // emit LockAdded(\n            //     ids[i],\n            //     token,\n            //     owners[i],\n            //     amounts[i],\n            //     vestingSettings[0] // TGE date\n            // );\n        }\n        _safeTransferFromEnsureExactAmount(\n            token,\n            msg.sender,\n            address(this),\n            sumAmount\n        );\n        return ids;\n    }\n\n    function _sumAmount(uint256[] calldata amounts)\n        internal\n        pure\n        returns (uint256)\n    {\n        uint256 sum = 0;\n        for (uint256 i = 0; i \u003c amounts.length; i++) {\n            if (amounts[i] == 0) {\n                revert(\"Amount cant be zero\");\n            }\n            sum += amounts[i];\n        }\n        return sum;\n    }\n\n    function _createLock(\n        address owner,\n        address token,\n        bool isLpToken,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) internal returns (uint256 id) {\n        if (isLpToken) {\n            address possibleFactoryAddress = _parseFactoryAddress(token);\n            id = _lockLpToken(\n                owner,\n                token,\n                possibleFactoryAddress,\n                amount,\n                tgeDate,\n                tgeBps,\n                cycle,\n                cycleBps,\n                description\n            );\n        } else {\n            id = _lockNormalToken(\n                owner,\n                token,\n                amount,\n                tgeDate,\n                tgeBps,\n                cycle,\n                cycleBps,\n                description\n            );\n        }\n        return id;\n    }\n\n    function _lockLpToken(\n        address owner,\n        address token,\n        address factory,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) private returns (uint256 id) {\n        id = _registerLock(\n            owner,\n            token,\n            amount,\n            tgeDate,\n            tgeBps,\n            cycle,\n            cycleBps,\n            description\n        );\n        _userLpLockIds[owner].add(id);\n        _lpLockedTokens.add(token);\n\n        CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[token];\n        if (tokenInfo.token == address(0)) {\n            tokenInfo.token = token;\n            tokenInfo.factory = factory;\n        }\n        tokenInfo.amount = tokenInfo.amount + amount;\n\n        _tokenToLockIds[token].add(id);\n    }\n\n    function _lockNormalToken(\n        address owner,\n        address token,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) private returns (uint256 id) {\n        id = _registerLock(\n            owner,\n            token,\n            amount,\n            tgeDate,\n            tgeBps,\n            cycle,\n            cycleBps,\n            description\n        );\n        _userNormalLockIds[owner].add(id);\n        _normalLockedTokens.add(token);\n\n        CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[token];\n        if (tokenInfo.token == address(0)) {\n            tokenInfo.token = token;\n            tokenInfo.factory = address(0);\n        }\n        tokenInfo.amount = tokenInfo.amount + amount;\n\n        _tokenToLockIds[token].add(id);\n    }\n\n    function _registerLock(\n        address owner,\n        address token,\n        uint256 amount,\n        uint256 tgeDate,\n        uint256 tgeBps,\n        uint256 cycle,\n        uint256 cycleBps,\n        string memory description\n    ) private returns (uint256 id) {\n        id = _locks.length + ID_PADDING;\n        Lock memory newLock = Lock({\n            id: id,\n            token: token,\n            owner: owner,\n            amount: amount,\n            lockDate: block.timestamp,\n            tgeDate: tgeDate,\n            tgeBps: tgeBps,\n            cycle: cycle,\n            cycleBps: cycleBps,\n            unlockedAmount: 0,\n            description: description\n        });\n        _locks.push(newLock);\n    }\n\n    function unlock(uint256 lockId) external override validLock(lockId) {\n        Lock storage userLock = _locks[_getActualIndex(lockId)];\n        require(\n            userLock.owner == msg.sender,\n            \"You are not the owner of this lock\"\n        );\n\n        if (userLock.tgeBps \u003e 0) {\n            _vestingUnlock(userLock);\n        } else {\n            _normalUnlock(userLock);\n        }\n    }\n\n    function _normalUnlock(Lock storage userLock) internal {\n        require(\n            block.timestamp \u003e= userLock.tgeDate,\n            \"It is not time to unlock\"\n        );\n        require(userLock.unlockedAmount == 0, \"Nothing to unlock\");\n\n        CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[\n            userLock.token\n        ];\n\n        bool isLpToken = tokenInfo.factory != address(0);\n\n        if (isLpToken) {\n            _userLpLockIds[msg.sender].remove(userLock.id);\n        } else {\n            _userNormalLockIds[msg.sender].remove(userLock.id);\n        }\n\n        uint256 unlockAmount = userLock.amount;\n\n        if (tokenInfo.amount \u003c= unlockAmount) {\n            tokenInfo.amount = 0;\n        } else {\n            tokenInfo.amount = tokenInfo.amount - unlockAmount;\n        }\n\n        if (tokenInfo.amount == 0) {\n            if (isLpToken) {\n                _lpLockedTokens.remove(userLock.token);\n            } else {\n                _normalLockedTokens.remove(userLock.token);\n            }\n        }\n        userLock.unlockedAmount = unlockAmount;\n\n        _tokenToLockIds[userLock.token].remove(userLock.id);\n\n        IERC20(userLock.token).safeTransfer(msg.sender, unlockAmount);\n\n        emit LockRemoved(\n            userLock.id,\n            userLock.token,\n            msg.sender,\n            unlockAmount,\n            block.timestamp\n        );\n    }\n\n    function _vestingUnlock(Lock storage userLock) internal {\n        uint256 withdrawable = _withdrawableTokens(userLock);\n        uint256 newTotalUnlockAmount = userLock.unlockedAmount + withdrawable;\n        require(\n            withdrawable \u003e 0 \u0026\u0026 newTotalUnlockAmount \u003c= userLock.amount,\n            \"Nothing to unlock\"\n        );\n\n        CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[\n            userLock.token\n        ];\n        bool isLpToken = tokenInfo.factory != address(0);\n\n        if (newTotalUnlockAmount == userLock.amount) {\n            if (isLpToken) {\n                _userLpLockIds[msg.sender].remove(userLock.id);\n            } else {\n                _userNormalLockIds[msg.sender].remove(userLock.id);\n            }\n            _tokenToLockIds[userLock.token].remove(userLock.id);\n            emit LockRemoved(\n                userLock.id,\n                userLock.token,\n                msg.sender,\n                newTotalUnlockAmount,\n                block.timestamp\n            );\n        }\n\n        if (tokenInfo.amount \u003c= withdrawable) {\n            tokenInfo.amount = 0;\n        } else {\n            tokenInfo.amount = tokenInfo.amount - withdrawable;\n        }\n\n        if (tokenInfo.amount == 0) {\n            if (isLpToken) {\n                _lpLockedTokens.remove(userLock.token);\n            } else {\n                _normalLockedTokens.remove(userLock.token);\n            }\n        }\n        userLock.unlockedAmount = newTotalUnlockAmount;\n\n        IERC20(userLock.token).safeTransfer(userLock.owner, withdrawable);\n\n        emit LockVested(\n            userLock.id,\n            userLock.token,\n            msg.sender,\n            withdrawable,\n            userLock.amount - userLock.unlockedAmount,\n            block.timestamp\n        );\n    }\n\n    function withdrawableTokens(uint256 lockId)\n        external\n        view\n        returns (uint256)\n    {\n        Lock memory userLock = getLockById(lockId);\n        return _withdrawableTokens(userLock);\n    }\n\n    function _withdrawableTokens(Lock memory userLock)\n        internal\n        view\n        returns (uint256)\n    {\n        if (userLock.amount == 0) return 0;\n        if (userLock.unlockedAmount \u003e= userLock.amount) return 0;\n        if (block.timestamp \u003c userLock.tgeDate) return 0;\n        if (userLock.cycle == 0) return 0;\n\n        uint256 tgeReleaseAmount = FullMath.mulDiv(\n            userLock.amount,\n            userLock.tgeBps,\n            10_000\n        );\n        uint256 cycleReleaseAmount = FullMath.mulDiv(\n            userLock.amount,\n            userLock.cycleBps,\n            10_000\n        );\n        uint256 currentTotal = 0;\n        if (block.timestamp \u003e= userLock.tgeDate) {\n            currentTotal =\n                (((block.timestamp - userLock.tgeDate) / userLock.cycle) *\n                    cycleReleaseAmount) +\n                tgeReleaseAmount; // Truncation is expected here\n        }\n        uint256 withdrawable = 0;\n        if (currentTotal \u003e userLock.amount) {\n            withdrawable = userLock.amount - userLock.unlockedAmount;\n        } else {\n            withdrawable = currentTotal - userLock.unlockedAmount;\n        }\n        return withdrawable;\n    }\n\n    function editLock(\n        uint256 lockId,\n        uint256 newAmount,\n        uint256 newUnlockDate\n    ) external override validLock(lockId) {\n        Lock storage userLock = _locks[_getActualIndex(lockId)];\n        require(\n            userLock.owner == msg.sender,\n            \"You are not the owner of this lock\"\n        );\n        require(userLock.unlockedAmount == 0, \"Lock was unlocked\");\n\n        if (newUnlockDate \u003e 0) {\n            require(\n                newUnlockDate \u003e= userLock.tgeDate \u0026\u0026\n                    newUnlockDate \u003e block.timestamp,\n                \"New unlock time should not be before old unlock time or current time\"\n            );\n            userLock.tgeDate = newUnlockDate;\n        }\n\n        if (newAmount \u003e 0) {\n            require(\n                newAmount \u003e= userLock.amount,\n                \"New amount should not be less than current amount\"\n            );\n\n            uint256 diff = newAmount - userLock.amount;\n\n            if (diff \u003e 0) {\n                userLock.amount = newAmount;\n                CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[\n                    userLock.token\n                ];\n                tokenInfo.amount = tokenInfo.amount + diff;\n                _safeTransferFromEnsureExactAmount(\n                    userLock.token,\n                    msg.sender,\n                    address(this),\n                    diff\n                );\n            }\n        }\n\n        emit LockUpdated(\n            userLock.id,\n            userLock.token,\n            userLock.owner,\n            userLock.amount,\n            userLock.tgeDate\n        );\n    }\n\n    function editLockDescription(uint256 lockId, string memory description)\n        external\n        validLock(lockId)\n    {\n        Lock storage userLock = _locks[_getActualIndex(lockId)];\n        require(\n            userLock.owner == msg.sender,\n            \"You are not the owner of this lock\"\n        );\n        userLock.description = description;\n        emit LockDescriptionChanged(lockId);\n    }\n\n    function transferLockOwnership(uint256 lockId, address newOwner)\n        public\n        validLock(lockId)\n    {\n        Lock storage userLock = _locks[_getActualIndex(lockId)];\n        address currentOwner = userLock.owner;\n        require(\n            currentOwner == msg.sender,\n            \"You are not the owner of this lock\"\n        );\n\n        userLock.owner = newOwner;\n\n        CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[\n            userLock.token\n        ];\n\n        bool isLpToken = tokenInfo.factory != address(0);\n\n        if (isLpToken) {\n            _userLpLockIds[currentOwner].remove(lockId);\n            _userLpLockIds[newOwner].add(lockId);\n        } else {\n            _userNormalLockIds[currentOwner].remove(lockId);\n            _userNormalLockIds[newOwner].add(lockId);\n        }\n\n        emit LockOwnerChanged(lockId, currentOwner, newOwner);\n    }\n\n    function renounceLockOwnership(uint256 lockId) external {\n        transferLockOwnership(lockId, address(0));\n    }\n\n    function _safeTransferFromEnsureExactAmount(\n        address token,\n        address sender,\n        address recipient,\n        uint256 amount\n    ) internal {\n        uint256 oldRecipientBalance = IERC20(token).balanceOf(recipient);\n        IERC20(token).safeTransferFrom(sender, recipient, amount);\n        uint256 newRecipientBalance = IERC20(token).balanceOf(recipient);\n        require(\n            newRecipientBalance - oldRecipientBalance == amount,\n            \"Not enough token was transfered\"\n        );\n    }\n\n    function getTotalLockCount() external view returns (uint256) {\n        // Returns total lock count, regardless of whether it has been unlocked or not\n        return _locks.length;\n    }\n\n    function getLockAt(uint256 index) external view returns (Lock memory) {\n        return _locks[index];\n    }\n\n    function getLockById(uint256 lockId) public view returns (Lock memory) {\n        return _locks[_getActualIndex(lockId)];\n    }\n\n    function allLpTokenLockedCount() public view returns (uint256) {\n        return _lpLockedTokens.length();\n    }\n\n    function allNormalTokenLockedCount() public view returns (uint256) {\n        return _normalLockedTokens.length();\n    }\n\n    function getCumulativeLpTokenLockInfoAt(uint256 index)\n        external\n        view\n        returns (CumulativeLockInfo memory)\n    {\n        return cumulativeLockInfo[_lpLockedTokens.at(index)];\n    }\n\n    function getCumulativeNormalTokenLockInfoAt(uint256 index)\n        external\n        view\n        returns (CumulativeLockInfo memory)\n    {\n        return cumulativeLockInfo[_normalLockedTokens.at(index)];\n    }\n\n    function getCumulativeLpTokenLockInfo(uint256 start, uint256 end)\n        external\n        view\n        returns (CumulativeLockInfo[] memory)\n    {\n        if (end \u003e= _lpLockedTokens.length()) {\n            end = _lpLockedTokens.length() - 1;\n        }\n        uint256 length = end - start + 1;\n        CumulativeLockInfo[] memory lockInfo = new CumulativeLockInfo[](length);\n        uint256 currentIndex = 0;\n        for (uint256 i = start; i \u003c= end; i++) {\n            lockInfo[currentIndex] = cumulativeLockInfo[_lpLockedTokens.at(i)];\n            currentIndex++;\n        }\n        return lockInfo;\n    }\n\n    function getCumulativeNormalTokenLockInfo(uint256 start, uint256 end)\n        external\n        view\n        returns (CumulativeLockInfo[] memory)\n    {\n        if (end \u003e= _normalLockedTokens.length()) {\n            end = _normalLockedTokens.length() - 1;\n        }\n        uint256 length = end - start + 1;\n        CumulativeLockInfo[] memory lockInfo = new CumulativeLockInfo[](length);\n        uint256 currentIndex = 0;\n        for (uint256 i = start; i \u003c= end; i++) {\n            lockInfo[currentIndex] = cumulativeLockInfo[\n                _normalLockedTokens.at(i)\n            ];\n            currentIndex++;\n        }\n        return lockInfo;\n    }\n\n    function totalTokenLockedCount() external view returns (uint256) {\n        return allLpTokenLockedCount() + allNormalTokenLockedCount();\n    }\n\n    function lpLockCountForUser(address user) public view returns (uint256) {\n        return _userLpLockIds[user].length();\n    }\n\n    function lpLocksForUser(address user)\n        external\n        view\n        returns (Lock[] memory)\n    {\n        uint256 length = _userLpLockIds[user].length();\n        Lock[] memory userLocks = new Lock[](length);\n        for (uint256 i = 0; i \u003c length; i++) {\n            userLocks[i] = getLockById(_userLpLockIds[user].at(i));\n        }\n        return userLocks;\n    }\n\n    function lpLockForUserAtIndex(address user, uint256 index)\n        external\n        view\n        returns (Lock memory)\n    {\n        require(lpLockCountForUser(user) \u003e index, \"Invalid index\");\n        return getLockById(_userLpLockIds[user].at(index));\n    }\n\n    function normalLockCountForUser(address user)\n        public\n        view\n        returns (uint256)\n    {\n        return _userNormalLockIds[user].length();\n    }\n\n    function normalLocksForUser(address user)\n        external\n        view\n        returns (Lock[] memory)\n    {\n        uint256 length = _userNormalLockIds[user].length();\n        Lock[] memory userLocks = new Lock[](length);\n\n        for (uint256 i = 0; i \u003c length; i++) {\n            userLocks[i] = getLockById(_userNormalLockIds[user].at(i));\n        }\n        return userLocks;\n    }\n\n    function normalLockForUserAtIndex(address user, uint256 index)\n        external\n        view\n        returns (Lock memory)\n    {\n        require(normalLockCountForUser(user) \u003e index, \"Invalid index\");\n        return getLockById(_userNormalLockIds[user].at(index));\n    }\n\n    function totalLockCountForUser(address user)\n        external\n        view\n        returns (uint256)\n    {\n        return normalLockCountForUser(user) + lpLockCountForUser(user);\n    }\n\n    function totalLockCountForToken(address token)\n        external\n        view\n        returns (uint256)\n    {\n        return _tokenToLockIds[token].length();\n    }\n\n    function getLocksForToken(\n        address token,\n        uint256 start,\n        uint256 end\n    ) public view returns (Lock[] memory) {\n        if (end \u003e= _tokenToLockIds[token].length()) {\n            end = _tokenToLockIds[token].length() - 1;\n        }\n        uint256 length = end - start + 1;\n        Lock[] memory locks = new Lock[](length);\n        uint256 currentIndex = 0;\n        for (uint256 i = start; i \u003c= end; i++) {\n            locks[currentIndex] = getLockById(_tokenToLockIds[token].at(i));\n            currentIndex++;\n        }\n        return locks;\n    }\n\n    function _getActualIndex(uint256 lockId) internal view returns (uint256) {\n        if (lockId \u003c ID_PADDING) {\n            revert(\"Invalid lock id\");\n        }\n        uint256 actualIndex = lockId - ID_PADDING;\n        require(actualIndex \u003c _locks.length, \"Invalid lock id\");\n        return actualIndex;\n    }\n\n    function _parseFactoryAddress(address token)\n        internal\n        view\n        returns (address)\n    {\n        address possibleFactoryAddress;\n        try IUniswapV2Pair(token).factory() returns (address factory) {\n            possibleFactoryAddress = factory;\n        } catch {\n            revert(\"This token is not a LP token\");\n        }\n        require(\n            possibleFactoryAddress != address(0) \u0026\u0026\n                _isValidLpToken(token, possibleFactoryAddress),\n            \"This token is not a LP token.\"\n        );\n        return possibleFactoryAddress;\n    }\n\n    function _isValidLpToken(address token, address factory)\n        private\n        view\n        returns (bool)\n    {\n        IUniswapV2Pair pair = IUniswapV2Pair(token);\n        address factoryPair = IUniswapV2Factory(factory).getPair(\n            pair.token0(),\n            pair.token1()\n        );\n        return factoryPair == token;\n    }\n}"},"SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // \u0027safeIncreaseAllowance\u0027 and \u0027safeDecreaseAllowance\u0027\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance \u003e= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity\u0027s return data size checking mechanism, since\n        // we\u0027re implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length \u003e 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}"}}