creation_sourcecode: // File: @openzeppelin/contracts/utils/Context.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Provides information about the current execution context, including the
   * sender of the transaction and its data. While these are generally available
   * via msg.sender and msg.data, they should not be accessed in such a direct
   * manner, since when dealing with meta-transactions the account sending and
   * paying for execution may not be the actual sender (as far as an application
   * is concerned).
   *
   * This contract is only required for intermediate, library-like contracts.
   */
  abstract contract Context {
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  
      function _contextSuffixLength() internal view virtual returns (uint256) {
          return 0;
      }
  }
  
  // File: @openzeppelin/contracts/access/Ownable.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
  
  pragma solidity ^0.8.20;
  
  
  /**
   * @dev Contract module which provides a basic access control mechanism, where
   * there is an account (an owner) that can be granted exclusive access to
   * specific functions.
   *
   * The initial owner is set to the address provided by the deployer. This can
   * later be changed with {transferOwnership}.
   *
   * This module is used through inheritance. It will make available the modifier
   * `onlyOwner`, which can be applied to your functions to restrict their use to
   * the owner.
   */
  abstract contract Ownable is Context {
      address private _owner;
  
      /**
       * @dev The caller account is not authorized to perform an operation.
       */
      error OwnableUnauthorizedAccount(address account);
  
      /**
       * @dev The owner is not a valid owner account. (eg. `address(0)`)
       */
      error OwnableInvalidOwner(address owner);
  
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  
      /**
       * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
       */
      constructor(address initialOwner) {
          if (initialOwner == address(0)) {
              revert OwnableInvalidOwner(address(0));
          }
          _transferOwnership(initialOwner);
      }
  
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
          _checkOwner();
          _;
      }
  
      /**
       * @dev Returns the address of the current owner.
       */
      function owner() public view virtual returns (address) {
          return _owner;
      }
  
      /**
       * @dev Throws if the sender is not the owner.
       */
      function _checkOwner() internal view virtual {
          if (owner() != _msgSender()) {
              revert OwnableUnauthorizedAccount(_msgSender());
          }
      }
  
      /**
       * @dev Leaves the contract without owner. It will not be possible to call
       * `onlyOwner` functions. Can only be called by the current owner.
       *
       * NOTE: Renouncing ownership will leave the contract without an owner,
       * thereby disabling any functionality that is only available to the owner.
       */
      function renounceOwnership() public virtual onlyOwner {
          _transferOwnership(address(0));
      }
  
      /**
       * @dev Transfers ownership of the contract to a new account (`newOwner`).
       * Can only be called by the current owner.
       */
      function transferOwnership(address newOwner) public virtual onlyOwner {
          if (newOwner == address(0)) {
              revert OwnableInvalidOwner(address(0));
          }
          _transferOwnership(newOwner);
      }
  
      /**
       * @dev Transfers ownership of the contract to a new account (`newOwner`).
       * Internal function without access restriction.
       */
      function _transferOwnership(address newOwner) internal virtual {
          address oldOwner = _owner;
          _owner = newOwner;
          emit OwnershipTransferred(oldOwner, newOwner);
      }
  }
  
  // File: @openzeppelin/contracts/utils/ReentrancyGuard.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Contract module that helps prevent reentrant calls to a function.
   *
   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
   * available, which can be applied to functions to make sure there are no nested
   * (reentrant) calls to them.
   *
   * Note that because there is a single `nonReentrant` guard, functions marked as
   * `nonReentrant` may not call one another. This can be worked around by making
   * those functions `private`, and then adding `external` `nonReentrant` entry
   * points to them.
   *
   * TIP: If you would like to learn more about reentrancy and alternative ways
   * to protect against it, check out our blog post
   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
   */
  abstract contract ReentrancyGuard {
      // Booleans are more expensive than uint256 or any type that takes up a full
      // word because each write operation emits an extra SLOAD to first read the
      // slot's contents, replace the bits taken up by the boolean, and then write
      // back. This is the compiler's defense against contract upgrades and
      // pointer aliasing, and it cannot be disabled.
  
      // The values being non-zero value makes deployment a bit more expensive,
      // but in exchange the refund on every call to nonReentrant will be lower in
      // amount. Since refunds are capped to a percentage of the total
      // transaction's gas, it is best to keep them low in cases like this one, to
      // increase the likelihood of the full refund coming into effect.
      uint256 private constant NOT_ENTERED = 1;
      uint256 private constant ENTERED = 2;
  
      uint256 private _status;
  
      /**
       * @dev Unauthorized reentrant call.
       */
      error ReentrancyGuardReentrantCall();
  
      constructor() {
          _status = NOT_ENTERED;
      }
  
      /**
       * @dev Prevents a contract from calling itself, directly or indirectly.
       * Calling a `nonReentrant` function from another `nonReentrant`
       * function is not supported. It is possible to prevent this from happening
       * by making the `nonReentrant` function external, and making it call a
       * `private` function that does the actual work.
       */
      modifier nonReentrant() {
          _nonReentrantBefore();
          _;
          _nonReentrantAfter();
      }
  
      function _nonReentrantBefore() private {
          // On the first call to nonReentrant, _status will be NOT_ENTERED
          if (_status == ENTERED) {
              revert ReentrancyGuardReentrantCall();
          }
  
          // Any calls to nonReentrant after this point will fail
          _status = ENTERED;
      }
  
      function _nonReentrantAfter() private {
          // By storing the original value once again, a refund is triggered (see
          // https://eips.ethereum.org/EIPS/eip-2200)
          _status = NOT_ENTERED;
      }
  
      /**
       * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
       * `nonReentrant` function in the call stack.
       */
      function _reentrancyGuardEntered() internal view returns (bool) {
          return _status == ENTERED;
      }
  }
  
  // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Interface of the ERC20 standard as defined in the EIP.
   */
  interface IERC20 {
      /**
       * @dev Emitted when `value` tokens are moved from one account (`from`) to
       * another (`to`).
       *
       * Note that `value` may be zero.
       */
      event Transfer(address indexed from, address indexed to, uint256 value);
  
      /**
       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
       * a call to {approve}. `value` is the new allowance.
       */
      event Approval(address indexed owner, address indexed spender, uint256 value);
  
      /**
       * @dev Returns the value of tokens in existence.
       */
      function totalSupply() external view returns (uint256);
  
      /**
       * @dev Returns the value of tokens owned by `account`.
       */
      function balanceOf(address account) external view returns (uint256);
  
      /**
       * @dev Moves a `value` amount of tokens from the caller's account to `to`.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transfer(address to, uint256 value) external returns (bool);
  
      /**
       * @dev Returns the remaining number of tokens that `spender` will be
       * allowed to spend on behalf of `owner` through {transferFrom}. This is
       * zero by default.
       *
       * This value changes when {approve} or {transferFrom} are called.
       */
      function allowance(address owner, address spender) external view returns (uint256);
  
      /**
       * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
       * caller's tokens.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * IMPORTANT: Beware that changing an allowance with this method brings the risk
       * that someone may use both the old and the new allowance by unfortunate
       * transaction ordering. One possible solution to mitigate this race
       * condition is to first reduce the spender's allowance to 0 and set the
       * desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       *
       * Emits an {Approval} event.
       */
      function approve(address spender, uint256 value) external returns (bool);
  
      /**
       * @dev Moves a `value` amount of tokens from `from` to `to` using the
       * allowance mechanism. `value` is then deducted from the caller's
       * allowance.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transferFrom(address from, address to, uint256 value) external returns (bool);
  }
  
  // File: @openzeppelin/contracts/utils/cryptography/ECDSA.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
   *
   * These functions can be used to verify that a message was signed by the holder
   * of the private keys of a given address.
   */
  library ECDSA {
      enum RecoverError {
          NoError,
          InvalidSignature,
          InvalidSignatureLength,
          InvalidSignatureS
      }
  
      /**
       * @dev The signature derives the `address(0)`.
       */
      error ECDSAInvalidSignature();
  
      /**
       * @dev The signature has an invalid length.
       */
      error ECDSAInvalidSignatureLength(uint256 length);
  
      /**
       * @dev The signature has an S value that is in the upper half order.
       */
      error ECDSAInvalidSignatureS(bytes32 s);
  
      /**
       * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
       * return address(0) without also returning an error description. Errors are documented using an enum (error type)
       * and a bytes32 providing additional information about the error.
       *
       * If no error is returned, then the address can be used for verification purposes.
       *
       * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
       * this function rejects them by requiring the `s` value to be in the lower
       * half order, and the `v` value to be either 27 or 28.
       *
       * IMPORTANT: `hash` _must_ be the result of a hash operation for the
       * verification to be secure: it is possible to craft signatures that
       * recover to arbitrary addresses for non-hashed data. A safe way to ensure
       * this is by receiving a hash of the original message (which may otherwise
       * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
       *
       * Documentation for signature generation:
       * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
       * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
       */
      function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
          if (signature.length == 65) {
              bytes32 r;
              bytes32 s;
              uint8 v;
              // ecrecover takes the signature parameters, and the only way to get them
              // currently is to use assembly.
              /// @solidity memory-safe-assembly
              assembly {
                  r := mload(add(signature, 0x20))
                  s := mload(add(signature, 0x40))
                  v := byte(0, mload(add(signature, 0x60)))
              }
              return tryRecover(hash, v, r, s);
          } else {
              return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
          }
      }
  
      /**
       * @dev Returns the address that signed a hashed message (`hash`) with
       * `signature`. This address can then be used for verification purposes.
       *
       * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
       * this function rejects them by requiring the `s` value to be in the lower
       * half order, and the `v` value to be either 27 or 28.
       *
       * IMPORTANT: `hash` _must_ be the result of a hash operation for the
       * verification to be secure: it is possible to craft signatures that
       * recover to arbitrary addresses for non-hashed data. A safe way to ensure
       * this is by receiving a hash of the original message (which may otherwise
       * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
       */
      function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
          (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
          _throwError(error, errorArg);
          return recovered;
      }
  
      /**
       * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
       *
       * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
       */
      function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
          unchecked {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              // We do not check for an overflow here since the shift operation results in 0 or 1.
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
      }
  
      /**
       * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
       */
      function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
          (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
          _throwError(error, errorArg);
          return recovered;
      }
  
      /**
       * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
       * `r` and `s` signature fields separately.
       */
      function tryRecover(
          bytes32 hash,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) internal pure returns (address, RecoverError, bytes32) {
          // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
          // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
          // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
          // signatures from current libraries generate a unique signature with an s-value in the lower half order.
          //
          // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
          // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
          // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
          // these malleable signatures as well.
          if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
              return (address(0), RecoverError.InvalidSignatureS, s);
          }
  
          // If the signature is valid (and not malleable), return the signer address
          address signer = ecrecover(hash, v, r, s);
          if (signer == address(0)) {
              return (address(0), RecoverError.InvalidSignature, bytes32(0));
          }
  
          return (signer, RecoverError.NoError, bytes32(0));
      }
  
      /**
       * @dev Overload of {ECDSA-recover} that receives the `v`,
       * `r` and `s` signature fields separately.
       */
      function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
          (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
          _throwError(error, errorArg);
          return recovered;
      }
  
      /**
       * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
       */
      function _throwError(RecoverError error, bytes32 errorArg) private pure {
          if (error == RecoverError.NoError) {
              return; // no error: do nothing
          } else if (error == RecoverError.InvalidSignature) {
              revert ECDSAInvalidSignature();
          } else if (error == RecoverError.InvalidSignatureLength) {
              revert ECDSAInvalidSignatureLength(uint256(errorArg));
          } else if (error == RecoverError.InvalidSignatureS) {
              revert ECDSAInvalidSignatureS(errorArg);
          }
      }
  }
  
  // File: @openzeppelin/contracts/utils/Pausable.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
  
  pragma solidity ^0.8.20;
  
  
  /**
   * @dev Contract module which allows children to implement an emergency stop
   * mechanism that can be triggered by an authorized account.
   *
   * This module is used through inheritance. It will make available the
   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
   * the functions of your contract. Note that they will not be pausable by
   * simply including this module, only once the modifiers are put in place.
   */
  abstract contract Pausable is Context {
      bool private _paused;
  
      /**
       * @dev Emitted when the pause is triggered by `account`.
       */
      event Paused(address account);
  
      /**
       * @dev Emitted when the pause is lifted by `account`.
       */
      event Unpaused(address account);
  
      /**
       * @dev The operation failed because the contract is paused.
       */
      error EnforcedPause();
  
      /**
       * @dev The operation failed because the contract is not paused.
       */
      error ExpectedPause();
  
      /**
       * @dev Initializes the contract in unpaused state.
       */
      constructor() {
          _paused = false;
      }
  
      /**
       * @dev Modifier to make a function callable only when the contract is not paused.
       *
       * Requirements:
       *
       * - The contract must not be paused.
       */
      modifier whenNotPaused() {
          _requireNotPaused();
          _;
      }
  
      /**
       * @dev Modifier to make a function callable only when the contract is paused.
       *
       * Requirements:
       *
       * - The contract must be paused.
       */
      modifier whenPaused() {
          _requirePaused();
          _;
      }
  
      /**
       * @dev Returns true if the contract is paused, and false otherwise.
       */
      function paused() public view virtual returns (bool) {
          return _paused;
      }
  
      /**
       * @dev Throws if the contract is paused.
       */
      function _requireNotPaused() internal view virtual {
          if (paused()) {
              revert EnforcedPause();
          }
      }
  
      /**
       * @dev Throws if the contract is not paused.
       */
      function _requirePaused() internal view virtual {
          if (!paused()) {
              revert ExpectedPause();
          }
      }
  
      /**
       * @dev Triggers stopped state.
       *
       * Requirements:
       *
       * - The contract must not be paused.
       */
      function _pause() internal virtual whenNotPaused {
          _paused = true;
          emit Paused(_msgSender());
      }
  
      /**
       * @dev Returns to normal state.
       *
       * Requirements:
       *
       * - The contract must be paused.
       */
      function _unpause() internal virtual whenPaused {
          _paused = false;
          emit Unpaused(_msgSender());
      }
  }
  
  // File: STAT-Lockup.sol
  
  
  pragma solidity ^0.8.0;
  
  
  
  
  
  
  /// @title STATLockUp
  /// @notice This contract allows users to lock up STAT tokens for a specified duration.
  /// @dev Implements token lockup functionality with EIP-712 signature verification.
  contract STATLockUp is ReentrancyGuard, Ownable, Pausable {
      using ECDSA for bytes32;
  
      IERC20 public token;
  
      mapping(address => uint256) public lockedUpAmounts; // The amount each user has locked up
      mapping(address => uint256) public lockupTimestamps; // The start time of the lockup for each user
  
      uint256 public requiredLockupAmount; // Required amount of STAT tokens to lock up
      uint256 public lockupDuration; // Duration of the lockup in seconds
      uint256 private totalLockedUpAmount; // Total amount of tokens locked up
  
      /// @notice Initializes the contract with the token address, required lockup amount, and lockup duration.
      /// @param _token The address of the STAT token contract.
      /// @param _lockupAmount The required amount of STAT tokens to lock up.
      /// @param _lockupDurationDays The duration of the lockup in days.
      constructor(address _token, uint256 _lockupAmount, uint256 _lockupDurationDays) 
          Ownable(msg.sender) {
          token = IERC20(_token);
          requiredLockupAmount = _lockupAmount;
          lockupDuration = _lockupDurationDays * 1 days; // Convert days to seconds
      }
  
      /// @notice Initiates a lockup for the caller's tokens.
      function statLockup(
      ) external nonReentrant whenNotPaused {
          require(lockedUpAmounts[msg.sender] == 0, "Already locked up");
  
          // Transfer tokens
          require(token.transferFrom(msg.sender, address(this), requiredLockupAmount), "Transfer failed");
  
          // Process the lockup
          lockedUpAmounts[msg.sender] = requiredLockupAmount;
          lockupTimestamps[msg.sender] = block.timestamp; // Lockup start time 
          totalLockedUpAmount += requiredLockupAmount; // Update total locked amount
      }
  
      /// @notice Allows users to withdraw their locked up tokens after the lockup period has ended.
      function withdrawLockup() external nonReentrant whenNotPaused {
          uint256 lockedAmount = lockedUpAmounts[msg.sender];
          require(lockedAmount > 0, "No locked up tokens to withdraw");
          require(block.timestamp >= lockupTimestamps[msg.sender] + lockupDuration, "Lockup period has not ended");
  
          require(token.transfer(msg.sender, lockedAmount), "Withdraw failed");
          lockedUpAmounts[msg.sender] = 0;
          lockupTimestamps[msg.sender] = 0;
          totalLockedUpAmount -= lockedAmount; // Update total locked amount
      }
  
      /// @notice Checks if an account has locked up tokens.
      /// @param account The address of the account to check.
      /// @return True if the account has locked up tokens, false otherwise.
      function hasLockedUp(address account) external view returns (bool) {
          return lockedUpAmounts[account] > 0;
      }
  
      /// @notice Retrieves the lockup duration for a specific account.
      /// @param account The address of the account.
      /// @return The lockup duration in seconds.
      function getLockupDuration(address account) external view returns (uint256) {
          require(lockedUpAmounts[account] > 0, "No locked up tokens");
          return block.timestamp - lockupTimestamps[account];
      }
  
      /// @notice Retrieves the remaining lockup time for a specific account.
      /// @param account The address of the account.
      /// @return The remaining lockup time in seconds.
      function getLockupTimeRemaining(address account) external view returns (uint256) {
          if (block.timestamp >= lockupTimestamps[account] + lockupDuration) {
              return 0;
          }
          return (lockupTimestamps[account] + lockupDuration) - block.timestamp;
      }
  
      // ---------- Administrator ---------- //
  
      /// @notice Updates the lockup duration.
      /// @param _durationDays The new lockup duration in days.
      /// @dev This function can only be called by the owner.
      function setLockupDuration(uint256 _durationDays) external onlyOwner {
          lockupDuration = _durationDays * 1 days; // Convert days to seconds
      }
  
      /// @notice Updates the required lockup amount.
      /// @param _amount The new required lockup amount.
      /// @dev This function can only be called by the owner.
      function setLockupAmount(uint256 _amount) external onlyOwner {
          requiredLockupAmount = _amount;
      }
  
      /// @notice Retrieves the total amount of locked up tokens.
      /// @return The total amount of locked up tokens.
      /// @dev This function can only be called by the owner.
      function getTotalLockedUpAmount() external view onlyOwner returns (uint256) {
          return totalLockedUpAmount;
      }
  
      /// @notice Updates the token contract address.
      /// @param newTokenContractAddress The new token contract address.
      /// @dev This function can only be called by the owner.
      function setTokenContractAddress(address newTokenContractAddress) external onlyOwner {
          require(newTokenContractAddress != address(0), "Invalid token address");
          token = IERC20(newTokenContractAddress);
      }
  
      /// @notice Pauses the contract.
      /// @dev This function can only be called by the owner.
      function pause() external onlyOwner {
          _pause();
      }
  
      /// @notice Unpauses the contract.
      /// @dev This function can only be called by the owner.
      function unpause() external onlyOwner {
          _unpause();
      }
  
      /// @notice Allows the owner to withdraw tokens in case of an emergency.
      /// @param _to The address to send the tokens to.
      /// @param _amount The amount of tokens to withdraw.
      /// @dev This function can only be called by the owner.
      function emergencyWithdraw(address _to, uint256 _amount) external onlyOwner {
          require(token.transfer(_to, _amount), "Withdraw failed");
      }
  }