creation_sourcecode: // File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
   *
   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
   * case an upgrade adds a module that needs to be initialized.
   *
   * For example:
   *
   * [.hljs-theme-light.nopadding]
   * ```solidity
   * contract MyToken is ERC20Upgradeable {
   *     function initialize() initializer public {
   *         __ERC20_init("MyToken", "MTK");
   *     }
   * }
   *
   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
   *     function initializeV2() reinitializer(2) public {
   *         __ERC20Permit_init("MyToken");
   *     }
   * }
   * ```
   *
   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
   *
   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
   *
   * [CAUTION]
   * ====
   * Avoid leaving a contract uninitialized.
   *
   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
   *
   * [.hljs-theme-light.nopadding]
   * ```
   * /// @custom:oz-upgrades-unsafe-allow constructor
   * constructor() {
   *     _disableInitializers();
   * }
   * ```
   * ====
   */
  abstract contract Initializable {
      /**
       * @dev Storage of the initializable contract.
       *
       * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
       * when using with upgradeable contracts.
       *
       * @custom:storage-location erc7201:openzeppelin.storage.Initializable
       */
      struct InitializableStorage {
          /**
           * @dev Indicates that the contract has been initialized.
           */
          uint64 _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool _initializing;
      }
  
      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
      bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
  
      /**
       * @dev The contract is already initialized.
       */
      error InvalidInitialization();
  
      /**
       * @dev The contract is not initializing.
       */
      error NotInitializing();
  
      /**
       * @dev Triggered when the contract has been initialized or reinitialized.
       */
      event Initialized(uint64 version);
  
      /**
       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
       * `onlyInitializing` functions can be used to initialize parent contracts.
       *
       * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
       * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
       * production.
       *
       * Emits an {Initialized} event.
       */
      modifier initializer() {
          // solhint-disable-next-line var-name-mixedcase
          InitializableStorage storage $ = _getInitializableStorage();
  
          // Cache values to avoid duplicated sloads
          bool isTopLevelCall = !$._initializing;
          uint64 initialized = $._initialized;
  
          // Allowed calls:
          // - initialSetup: the contract is not in the initializing state and no previous version was
          //                 initialized
          // - construction: the contract is initialized at version 1 (no reininitialization) and the
          //                 current contract is just being deployed
          bool initialSetup = initialized == 0 && isTopLevelCall;
          bool construction = initialized == 1 && address(this).code.length == 0;
  
          if (!initialSetup && !construction) {
              revert InvalidInitialization();
          }
          $._initialized = 1;
          if (isTopLevelCall) {
              $._initializing = true;
          }
          _;
          if (isTopLevelCall) {
              $._initializing = false;
              emit Initialized(1);
          }
      }
  
      /**
       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
       * used to initialize parent contracts.
       *
       * A reinitializer may be used after the original initialization step. This is essential to configure modules that
       * are added through upgrades and that require initialization.
       *
       * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
       * cannot be nested. If one is invoked in the context of another, execution will revert.
       *
       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
       * a contract, executing them in the right order is up to the developer or operator.
       *
       * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
       *
       * Emits an {Initialized} event.
       */
      modifier reinitializer(uint64 version) {
          // solhint-disable-next-line var-name-mixedcase
          InitializableStorage storage $ = _getInitializableStorage();
  
          if ($._initializing || $._initialized >= version) {
              revert InvalidInitialization();
          }
          $._initialized = version;
          $._initializing = true;
          _;
          $._initializing = false;
          emit Initialized(version);
      }
  
      /**
       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
       * {initializer} and {reinitializer} modifiers, directly or indirectly.
       */
      modifier onlyInitializing() {
          _checkInitializing();
          _;
      }
  
      /**
       * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
       */
      function _checkInitializing() internal view virtual {
          if (!_isInitializing()) {
              revert NotInitializing();
          }
      }
  
      /**
       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
       * through proxies.
       *
       * Emits an {Initialized} event the first time it is successfully executed.
       */
      function _disableInitializers() internal virtual {
          // solhint-disable-next-line var-name-mixedcase
          InitializableStorage storage $ = _getInitializableStorage();
  
          if ($._initializing) {
              revert InvalidInitialization();
          }
          if ($._initialized != type(uint64).max) {
              $._initialized = type(uint64).max;
              emit Initialized(type(uint64).max);
          }
      }
  
      /**
       * @dev Returns the highest version that has been initialized. See {reinitializer}.
       */
      function _getInitializedVersion() internal view returns (uint64) {
          return _getInitializableStorage()._initialized;
      }
  
      /**
       * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
       */
      function _isInitializing() internal view returns (bool) {
          return _getInitializableStorage()._initializing;
      }
  
      /**
       * @dev Returns a pointer to the storage namespace.
       */
      // solhint-disable-next-line var-name-mixedcase
      function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
          assembly {
              $.slot := INITIALIZABLE_STORAGE
          }
      }
  }
  
  // File: @openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol
  
  
  // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
  
  pragma solidity ^0.8.0;
  
  
  /**
   * @dev Contract module that helps prevent reentrant calls to a function.
   *
   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
   * available, which can be applied to functions to make sure there are no nested
   * (reentrant) calls to them.
   *
   * Note that because there is a single `nonReentrant` guard, functions marked as
   * `nonReentrant` may not call one another. This can be worked around by making
   * those functions `private`, and then adding `external` `nonReentrant` entry
   * points to them.
   *
   * TIP: If you would like to learn more about reentrancy and alternative ways
   * to protect against it, check out our blog post
   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
   */
  abstract contract ReentrancyGuardUpgradeable is Initializable {
      // 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;
  
      function __ReentrancyGuard_init() internal onlyInitializing {
          __ReentrancyGuard_init_unchained();
      }
  
      function __ReentrancyGuard_init_unchained() internal onlyInitializing {
          _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
          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
  
          // 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;
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[49] private __gap;
  }
  
  // File: @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.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 ContextUpgradeable is Initializable {
      function __Context_init() internal onlyInitializing {
      }
  
      function __Context_init_unchained() internal onlyInitializing {
      }
      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-upgradeable/access/OwnableUpgradeable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
      /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
      struct OwnableStorage {
          address _owner;
      }
  
      // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
      bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
  
      function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
          assembly {
              $.slot := OwnableStorageLocation
          }
      }
  
      /**
       * @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.
       */
      function __Ownable_init(address initialOwner) internal onlyInitializing {
          __Ownable_init_unchained(initialOwner);
      }
  
      function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
          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) {
          OwnableStorage storage $ = _getOwnableStorage();
          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 {
          OwnableStorage storage $ = _getOwnableStorage();
          address oldOwner = $._owner;
          $._owner = newOwner;
          emit OwnershipTransferred(oldOwner, newOwner);
      }
  }
  
  // File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol
  
  
  // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
  // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Library for managing
   * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
   * types.
   *
   * Sets have the following properties:
   *
   * - Elements are added, removed, and checked for existence in constant time
   * (O(1)).
   * - Elements are enumerated in O(n). No guarantees are made on the ordering.
   *
   * ```solidity
   * contract Example {
   *     // Add the library methods
   *     using EnumerableSet for EnumerableSet.AddressSet;
   *
   *     // Declare a set state variable
   *     EnumerableSet.AddressSet private mySet;
   * }
   * ```
   *
   * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
   * and `uint256` (`UintSet`) are supported.
   *
   * [WARNING]
   * ====
   * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
   * unusable.
   * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
   *
   * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
   * array of EnumerableSet.
   * ====
   */
  library EnumerableSet {
      // To implement this library for multiple types with as little code
      // repetition as possible, we write it in terms of a generic Set type with
      // bytes32 values.
      // The Set implementation uses private functions, and user-facing
      // implementations (such as AddressSet) are just wrappers around the
      // underlying Set.
      // This means that we can only create new EnumerableSets for types that fit
      // in bytes32.
  
      struct Set {
          // Storage of set values
          bytes32[] _values;
          // Position is the index of the value in the `values` array plus 1.
          // Position 0 is used to mean a value is not in the set.
          mapping(bytes32 value => uint256) _positions;
      }
  
      /**
       * @dev Add a value to a set. O(1).
       *
       * Returns true if the value was added to the set, that is if it was not
       * already present.
       */
      function _add(Set storage set, bytes32 value) private returns (bool) {
          if (!_contains(set, value)) {
              set._values.push(value);
              // The value is stored at length-1, but we add 1 to all indexes
              // and use 0 as a sentinel value
              set._positions[value] = set._values.length;
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * @dev Removes a value from a set. O(1).
       *
       * Returns true if the value was removed from the set, that is if it was
       * present.
       */
      function _remove(Set storage set, bytes32 value) private returns (bool) {
          // We cache the value's position to prevent multiple reads from the same storage slot
          uint256 position = set._positions[value];
  
          if (position != 0) {
              // Equivalent to contains(set, value)
              // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
              // the array, and then remove the last element (sometimes called as 'swap and pop').
              // This modifies the order of the array, as noted in {at}.
  
              uint256 valueIndex = position - 1;
              uint256 lastIndex = set._values.length - 1;
  
              if (valueIndex != lastIndex) {
                  bytes32 lastValue = set._values[lastIndex];
  
                  // Move the lastValue to the index where the value to delete is
                  set._values[valueIndex] = lastValue;
                  // Update the tracked position of the lastValue (that was just moved)
                  set._positions[lastValue] = position;
              }
  
              // Delete the slot where the moved value was stored
              set._values.pop();
  
              // Delete the tracked position for the deleted slot
              delete set._positions[value];
  
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * @dev Returns true if the value is in the set. O(1).
       */
      function _contains(Set storage set, bytes32 value) private view returns (bool) {
          return set._positions[value] != 0;
      }
  
      /**
       * @dev Returns the number of values on the set. O(1).
       */
      function _length(Set storage set) private view returns (uint256) {
          return set._values.length;
      }
  
      /**
       * @dev Returns the value stored at position `index` in the set. O(1).
       *
       * Note that there are no guarantees on the ordering of values inside the
       * array, and it may change when more values are added or removed.
       *
       * Requirements:
       *
       * - `index` must be strictly less than {length}.
       */
      function _at(Set storage set, uint256 index) private view returns (bytes32) {
          return set._values[index];
      }
  
      /**
       * @dev Return the entire set in an array
       *
       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
       */
      function _values(Set storage set) private view returns (bytes32[] memory) {
          return set._values;
      }
  
      // Bytes32Set
  
      struct Bytes32Set {
          Set _inner;
      }
  
      /**
       * @dev Add a value to a set. O(1).
       *
       * Returns true if the value was added to the set, that is if it was not
       * already present.
       */
      function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
          return _add(set._inner, value);
      }
  
      /**
       * @dev Removes a value from a set. O(1).
       *
       * Returns true if the value was removed from the set, that is if it was
       * present.
       */
      function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
          return _remove(set._inner, value);
      }
  
      /**
       * @dev Returns true if the value is in the set. O(1).
       */
      function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
          return _contains(set._inner, value);
      }
  
      /**
       * @dev Returns the number of values in the set. O(1).
       */
      function length(Bytes32Set storage set) internal view returns (uint256) {
          return _length(set._inner);
      }
  
      /**
       * @dev Returns the value stored at position `index` in the set. O(1).
       *
       * Note that there are no guarantees on the ordering of values inside the
       * array, and it may change when more values are added or removed.
       *
       * Requirements:
       *
       * - `index` must be strictly less than {length}.
       */
      function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
          return _at(set._inner, index);
      }
  
      /**
       * @dev Return the entire set in an array
       *
       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
       */
      function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
          bytes32[] memory store = _values(set._inner);
          bytes32[] memory result;
  
          /// @solidity memory-safe-assembly
          assembly {
              result := store
          }
  
          return result;
      }
  
      // AddressSet
  
      struct AddressSet {
          Set _inner;
      }
  
      /**
       * @dev Add a value to a set. O(1).
       *
       * Returns true if the value was added to the set, that is if it was not
       * already present.
       */
      function add(AddressSet storage set, address value) internal returns (bool) {
          return _add(set._inner, bytes32(uint256(uint160(value))));
      }
  
      /**
       * @dev Removes a value from a set. O(1).
       *
       * Returns true if the value was removed from the set, that is if it was
       * present.
       */
      function remove(AddressSet storage set, address value) internal returns (bool) {
          return _remove(set._inner, bytes32(uint256(uint160(value))));
      }
  
      /**
       * @dev Returns true if the value is in the set. O(1).
       */
      function contains(AddressSet storage set, address value) internal view returns (bool) {
          return _contains(set._inner, bytes32(uint256(uint160(value))));
      }
  
      /**
       * @dev Returns the number of values in the set. O(1).
       */
      function length(AddressSet storage set) internal view returns (uint256) {
          return _length(set._inner);
      }
  
      /**
       * @dev Returns the value stored at position `index` in the set. O(1).
       *
       * Note that there are no guarantees on the ordering of values inside the
       * array, and it may change when more values are added or removed.
       *
       * Requirements:
       *
       * - `index` must be strictly less than {length}.
       */
      function at(AddressSet storage set, uint256 index) internal view returns (address) {
          return address(uint160(uint256(_at(set._inner, index))));
      }
  
      /**
       * @dev Return the entire set in an array
       *
       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
       */
      function values(AddressSet storage set) internal view returns (address[] memory) {
          bytes32[] memory store = _values(set._inner);
          address[] memory result;
  
          /// @solidity memory-safe-assembly
          assembly {
              result := store
          }
  
          return result;
      }
  
      // UintSet
  
      struct UintSet {
          Set _inner;
      }
  
      /**
       * @dev Add a value to a set. O(1).
       *
       * Returns true if the value was added to the set, that is if it was not
       * already present.
       */
      function add(UintSet storage set, uint256 value) internal returns (bool) {
          return _add(set._inner, bytes32(value));
      }
  
      /**
       * @dev Removes a value from a set. O(1).
       *
       * Returns true if the value was removed from the set, that is if it was
       * present.
       */
      function remove(UintSet storage set, uint256 value) internal returns (bool) {
          return _remove(set._inner, bytes32(value));
      }
  
      /**
       * @dev Returns true if the value is in the set. O(1).
       */
      function contains(UintSet storage set, uint256 value) internal view returns (bool) {
          return _contains(set._inner, bytes32(value));
      }
  
      /**
       * @dev Returns the number of values in the set. O(1).
       */
      function length(UintSet storage set) internal view returns (uint256) {
          return _length(set._inner);
      }
  
      /**
       * @dev Returns the value stored at position `index` in the set. O(1).
       *
       * Note that there are no guarantees on the ordering of values inside the
       * array, and it may change when more values are added or removed.
       *
       * Requirements:
       *
       * - `index` must be strictly less than {length}.
       */
      function at(UintSet storage set, uint256 index) internal view returns (uint256) {
          return uint256(_at(set._inner, index));
      }
  
      /**
       * @dev Return the entire set in an array
       *
       * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
       * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
       * this function has an unbounded cost, and using it as part of a state-changing function may render the function
       * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
       */
      function values(UintSet storage set) internal view returns (uint256[] memory) {
          bytes32[] memory store = _values(set._inner);
          uint256[] memory result;
  
          /// @solidity memory-safe-assembly
          assembly {
              result := store
          }
  
          return result;
      }
  }
  
  // File: utils/AllowableAddressUpgradeable.sol
  
  
  pragma solidity ^0.8.4;
  
  
  
  contract AllowableAddress is OwnableUpgradeable {
  
      using EnumerableSet for EnumerableSet.AddressSet;
  
      // @dev the allowed minter addresses
      EnumerableSet.AddressSet internal s_minters;
      // @dev the allowed burner addresses
      EnumerableSet.AddressSet internal s_burners;
  
      /******* Variables *******/
      // blacklist address => bool
      mapping(address => bool) internal blacklistAddress;
  
      /******* Modifiers *******/
      modifier blacklistCheck(address _from, address _to) {
          require(!blacklistAddress[_from] && !blacklistAddress[_to], "Address is blacklisted");
          _;
      }
  
      modifier onlyMinter() {
          if (!isMinter(msg.sender)) revert SenderNotMinter(msg.sender);
          _;
      }
  
      modifier onlyBurner() {
          if (!isBurner(msg.sender)) revert SenderNotBurner(msg.sender);
          _;
      }
  
      /******* Events *******/
      error SenderNotMinter(address sender);
      error SenderNotBurner(address sender);
      error MaxSupplyExceeded(uint256 supplyAfterMint);
  
      event MintAccessGranted(address indexed minter);
      event BurnAccessGranted(address indexed burner);
      event MintAccessRevoked(address indexed minter);
      event BurnAccessRevoked(address indexed burner);
      event BlacklistUpdated(bool status);
  
      /******* Constructor *******/
      function __AllowableAddress_init(address _ownerAddress) internal {
          __Ownable_init(_ownerAddress);
      }
  
      /*
       * @notice Manage blacklisting to restrict addresses
       * @param addresses     Array of addresses
       * @param status        If blacklist passing true, false otherwise.
       */
      function manageBlacklist(address[] calldata addresses, bool status)
          external
          virtual
          onlyOwner
      {
          for (uint256 i; i < addresses.length; ++i) {
              blacklistAddress[addresses[i]] = status;
          }
          emit BlacklistUpdated(status);
      }
  
      /*
       * @notice grants both mint and burn roles to `burnAndMinter`.
       * @notice Accessable to only owner.
       * @param burnAndMinter      Any address for mint and burn access
       */
      function grantMintAndBurnRoles(address burnAndMinter) external onlyOwner {
          grantMintRole(burnAndMinter);
          grantBurnRole(burnAndMinter);
      }
  
      /*
       * @notice Grants mint role to the given address.
       * @notice Accessable to only owner.
       * @param minter    Any address for mint access
       */
      function grantMintRole(address minter) public onlyOwner {
          if (s_minters.add(minter)) {
              emit MintAccessGranted(minter);
          }
      }
  
      /*
       * @notice Grants burn role to the given address.
       * @notice Accessable to only owner.
       * @param burner    Any address for burn access
       */
      function grantBurnRole(address burner) public onlyOwner {
          if (s_burners.add(burner)) {
              emit BurnAccessGranted(burner);
          }
      }
  
      /*
       * @notice Revokes mint role for the given address.
       * @notice Accessable to only owner.
       * @param minter    Any address for remove mint access
       */
      function revokeMintRole(address minter) public onlyOwner {
          if (s_minters.remove(minter)) {
              emit MintAccessRevoked(minter);
          }
      }
  
      /*
       * @notice Revokes burn role from the given address.
       * @notice Accessable to only owner
       * @param burner    Any address for remove burn access
       */
      function revokeBurnRole(address burner) public onlyOwner {
          if (s_burners.remove(burner)) {
              emit BurnAccessRevoked(burner);
          }
      }
  
      /*
       * @param _address      Any address
       * @return {the address is blacklisted or not}.
       */
      function isBlacklisted(address _address) public view returns (bool) {
          return blacklistAddress[_address];
      }
  
      /*
       * @return {all permissioned minters}
       */
      function getMinters() public view returns (address[] memory) {
          return s_minters.values();
      }
  
      /*
       * @return {all permissioned burners}
       */
      function getBurners() public view returns (address[] memory) {
          return s_burners.values();
      }
  
      /*
       * @notice Checks whether a given address is a minter for this token.
       * @return {true if the address is allowed to mint}.
       */
      function isMinter(address minter) public view returns (bool) {
          return s_minters.contains(minter);
      }
  
      /*
       * @notice Checks whether a given address is a burner for this token.
       * @return {true if the address is allowed to burn}.
       */
      function isBurner(address burner) public view returns (bool) {
          return s_burners.contains(burner);
      }
  }
  
  // File: @openzeppelin/contracts/utils/math/SafeMath.sol
  
  
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)
  
  pragma solidity ^0.8.0;
  
  // CAUTION
  // This version of SafeMath should only be used with Solidity 0.8 or later,
  // because it relies on the compiler's built in overflow checks.
  
  /**
   * @dev Wrappers over Solidity's arithmetic operations.
   *
   * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
   * now has built in overflow checking.
   */
  library SafeMath {
      /**
       * @dev Returns the addition of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
          unchecked {
              uint256 c = a + b;
              if (c < a) return (false, 0);
              return (true, c);
          }
      }
  
      /**
       * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
          unchecked {
              if (b > a) return (false, 0);
              return (true, a - b);
          }
      }
  
      /**
       * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
          unchecked {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) return (true, 0);
              uint256 c = a * b;
              if (c / a != b) return (false, 0);
              return (true, c);
          }
      }
  
      /**
       * @dev Returns the division of two unsigned integers, with a division by zero flag.
       *
       * _Available since v3.4._
       */
      function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
          unchecked {
              if (b == 0) return (false, 0);
              return (true, a / b);
          }
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
       *
       * _Available since v3.4._
       */
      function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
          unchecked {
              if (b == 0) return (false, 0);
              return (true, a % b);
          }
      }
  
      /**
       * @dev Returns the addition of two unsigned integers, reverting on
       * overflow.
       *
       * Counterpart to Solidity's `+` operator.
       *
       * Requirements:
       *
       * - Addition cannot overflow.
       */
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
          return a + b;
      }
  
      /**
       * @dev Returns the subtraction of two unsigned integers, reverting on
       * overflow (when the result is negative).
       *
       * Counterpart to Solidity's `-` operator.
       *
       * Requirements:
       *
       * - Subtraction cannot overflow.
       */
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          return a - b;
      }
  
      /**
       * @dev Returns the multiplication of two unsigned integers, reverting on
       * overflow.
       *
       * Counterpart to Solidity's `*` operator.
       *
       * Requirements:
       *
       * - Multiplication cannot overflow.
       */
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
          return a * b;
      }
  
      /**
       * @dev Returns the integer division of two unsigned integers, reverting on
       * division by zero. The result is rounded towards zero.
       *
       * Counterpart to Solidity's `/` operator.
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
          return a / b;
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
       * reverting when dividing by zero.
       *
       * Counterpart to Solidity's `%` operator. This function uses a `revert`
       * opcode (which leaves remaining gas untouched) while Solidity uses an
       * invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
          return a % b;
      }
  
      /**
       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
       * overflow (when the result is negative).
       *
       * CAUTION: This function is deprecated because it requires allocating memory for the error
       * message unnecessarily. For custom revert reasons use {trySub}.
       *
       * Counterpart to Solidity's `-` operator.
       *
       * Requirements:
       *
       * - Subtraction cannot overflow.
       */
      function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
          unchecked {
              require(b <= a, errorMessage);
              return a - b;
          }
      }
  
      /**
       * @dev Returns the integer division of two unsigned integers, reverting with custom message on
       * division by zero. The result is rounded towards zero.
       *
       * Counterpart to Solidity's `/` operator. Note: this function uses a
       * `revert` opcode (which leaves remaining gas untouched) while Solidity
       * uses an invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
          unchecked {
              require(b > 0, errorMessage);
              return a / b;
          }
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
       * reverting with custom message when dividing by zero.
       *
       * CAUTION: This function is deprecated because it requires allocating memory for the error
       * message unnecessarily. For custom revert reasons use {tryMod}.
       *
       * Counterpart to Solidity's `%` operator. This function uses a `revert`
       * opcode (which leaves remaining gas untouched) while Solidity uses an
       * invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
          unchecked {
              require(b > 0, errorMessage);
              return a % b;
          }
      }
  }
  
  // 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: StakingDrift2.sol
  
  /**
   * SPDX-License-Identifier: Apache-2.0
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * Copyright (c) 2024, DRIFTToken.
   *
   * DRIFT is the studio token behind the Drift studio. Drift is a Web3 game studio, which already has a ready-to-play Beta version of its game, Payout Pursuit.
   *
   * The DRIFT token has utility for gamers and non-gamers. Gamers that hold DRIFT will be able to customize their gaming experience with NFT skins and other features.
   * Non-gamers who stake DRIFT receive a percentage of game revenue, and the token also receives Liquidity from a percentage of game revenue.
   *
   * The Drift project has been developed by a highly experienced, fully doxxed Web3 team, with multiple successes in the Web3 space.
   *
   * Drift:
   * https://drifttoken.io/
   *
   * Influ3nce:
   * https://influ3nce.me/
   *
   * Amba$$ador:
   * https://influ3nce.me/ambassador/
   *
   * Twitter / X:
   * https://twitter.com/TheDriftToken
   *
   * Telegram:
   * https://t.me/driftportal
   *
   */
  
  pragma solidity 0.8.20;
  
  
  
  
  
  // Interface of ERC20
  interface IERC20_EXT is IERC20 {
      function mint(address to, uint256 amount) external;
  
      function burn(uint256 value) external;
  
      function burnFrom(address account, uint256 value) external;
  
      function permit(
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external;
  
      function decimals() external view returns (uint8);
  }
  
  // Entry of Staking Drift Pool Contract
  contract StakingDrift is AllowableAddress, ReentrancyGuardUpgradeable {
      using SafeMath for uint256;
  
      struct User {
          uint256 stakedBalance;
          uint256 lastStakedTime;
          uint256 lastUpdateTime;
          uint256 rewardsBalance;
          uint256 rewardRate;
      }
  
      struct StakeInfo {
          uint256 minimumStake;
          uint256 maximumStake;
          uint256 minStakePeriod;
          uint256 fixedAPY; // 18 decimals
          uint256 lockedStake; // Days
          bool fixedRewardRate;
      }
  
      /* Variables */
      bool public initialized = false;
      string public version;
  
      IERC20_EXT public stDriftToken; // 1:1 Stake token to be issued equals to staked drift
      IERC20_EXT public driftToken; // Drift token to be stake
      IERC20_EXT public rewardToken; // Reward Token
      address public factory; // Deployer factory
      address payable public feeWallet; // Fee wallet address where the fees will be sent to.
  
      StakeInfo public stakeInfo; // Info of this Stake Pool
  
      bool private isNative = false; // Is Reward is Native Gas Token
      bool private openStake = false; // Is stake pool open
  
      uint256 public totalStaked; // Total Stake Amount
      uint256 public unstakeFee; // Percentage multiplication with 1000.
      uint256 public rewardRate; // Reward rate per token per day (required if Fixed APY enabled)
      uint256 public stakeStartTimestamp = 0; // Stake Start TimeStamp for rewardRate
      uint256 public stakeEndDeadline = 0; // Stake End deadline for rewardRate and/or pool. If zero, no end period.
      uint256 public BONUS_MULTIPLIER; // e.g. Rewards (2x, 3x etc)
  
      mapping(address => User) private users; // Mapping User address => User info
  
      /* Modifiers */
      modifier checkAmount(uint256 _amount) {
          require(
              _amount >= stakeInfo.minimumStake && _amount <= stakeInfo.maximumStake,
              "StakingDrift: invalid stake amount"
          );
          _;
      }
  
      modifier isOpen() {
          require(isOpenStake(), "StakingDrift: stake not opened");
          _;
      }
  
      /* Events */
      event Staked(address indexed user, uint256 amount);
      event Unstaked(address indexed user, uint256 amount);
      event Claimed(address indexed user, uint256 amount);
      event UpdatedDriftToken(address driftToken);
      event UpdatedStakeDriftToken(address stDriftToken);
      event UpdatedFeeWallet(address feeWallet);
      event UpdatedBonusMultiplier(uint8 _multiplier);
      event RemoveFixed(uint256 timestamp);
      event EnableFixed(uint256 timestamp);
      event StakeOpen(uint256 stakeEndDeadlineInDays, uint256 unstakeFeeRate, uint256 timestamp);
      event StakeClosed(uint256 timestamp);
      event UpdateUnstakeFeesAndLockedDays(uint256 unstakeFeeRate, uint256 lockedStakeDays);
      event UpdateLimits(uint256 minimumStake, uint256 maximumStake, uint256 minStakePeriod);
  
  
      /* Constructor */
      /**
       * @notice Initialize Construction called from proxy initialization
       * @param _factory                  Deployer factory address
       * @param _driftToken               Drift Token Address
       * @param _stDriftToken             Stake Drift Token Address
       * @param _rewardToken              Reward Token Address
       * @param _feeWallet                Fee Wallet Address
       * @param _isRewardNativeToken      True if Reward is Native, false otherwise.
       * @param _fixedAPY                 Percentage in wei if enable Fixed APY, zero otherwise.
       * @param _rewardRate               Reward rate is required if Fixed APY enabled. Reward rate is a per token per day.
       * @param _lockedStake              For Locked Stake, value in days greater than zero. Zero otherwise.
       */
      function initialize(
          address _factory,
          address _driftToken,
          address _stDriftToken,
          address _rewardToken,
          address _feeWallet,
          bool _isRewardNativeToken,
          uint256 _fixedAPY, // in wei
          uint256 _rewardRate, // in wei
          uint256 _lockedStake
      ) public initializer {
          require(!initialized, "Already initialized");
  
          __AllowableAddress_init(_msgSender());
  
          factory = _factory;
          feeWallet = payable(_feeWallet);
          driftToken = IERC20_EXT(_driftToken);
          stDriftToken = IERC20_EXT(_stDriftToken);
          rewardToken = IERC20_EXT(_rewardToken);
          isNative = _isRewardNativeToken;
          stakeInfo.fixedAPY = _fixedAPY;
          if(_fixedAPY > 0) {
              require(_rewardRate > 0, "StakingDrift: reward rate per token per day required for fixed APY");
          }
          if(_rewardRate > 0) {
              rewardRate = _rewardRate;
              stakeInfo.fixedRewardRate = true;
          }
          stakeInfo.maximumStake = type(uint256).max;
          stakeInfo.minStakePeriod = 30;
          stakeInfo.lockedStake = _lockedStake;
          BONUS_MULTIPLIER = 1;
  
          version = "1";
          initialized = true;
      }
  
      /*
       * @notice fallback functions
       */
      receive() external payable {}
  
      fallback() external payable {}
  
      /**
       * @notice Update Bonus Multiplier
       * @notice Accessable to only owner
       * @param _multiplier       Multiplier in number (i.e. 2 = 2x, 3 = 3x, etc)
       */
      function updateBonusMultiplier(uint8 _multiplier) external onlyOwner {
          require(
              _multiplier >= 1,
              "StakingDrift: bonus multiplier should be greater than or equals to 1"
          );
          BONUS_MULTIPLIER = _multiplier;
          emit UpdatedBonusMultiplier(_multiplier);
      }
  
      /**
       * @notice Update Drift Token Address
       * @notice Accessable to only owner
       * @param _newDriftToken       New Drift Token Address
       */
      function updateDriftToken(address _newDriftToken) external onlyOwner {
          require(_newDriftToken != address(0), "StakingDrift: newDriftToken is a zero address");
          driftToken = IERC20_EXT(_newDriftToken);
          emit UpdatedDriftToken(_newDriftToken);
      }
  
      /**
       * @notice Update Fee Wallet
       * @notice Accessable to only owner
       * @param _newStDriftToken       New Stake Drift Token Address
       */
      function updateStakeDriftToken(address _newStDriftToken) external onlyOwner {
          require(_newStDriftToken != address(0), "StakingDrift: newStDriftToken is a zero address");
          stDriftToken = IERC20_EXT(_newStDriftToken);
          emit UpdatedStakeDriftToken(_newStDriftToken);
      }
  
      /**
       * @notice Update Fee Wallet
       * @notice Accessable to only owner
       * @param _newFeeWallet       New Fee Wallet Address
       */
      function updateFeeWallet(address _newFeeWallet) external onlyOwner {
          require(_newFeeWallet != address(0), "StakingDrift: newFeeWallet is a zero address");
          feeWallet = payable(_newFeeWallet);
          emit UpdatedFeeWallet(_newFeeWallet);
      }
  
      /**
       * @notice Remove fixed APY, reward rate
       * @notice Accessable to only owner
       */
      function removeFixed() external onlyOwner {
          stakeInfo.fixedAPY = 0;
          stakeInfo.fixedRewardRate = false;
          calculateRewardRate();
          emit RemoveFixed(block.timestamp);
      }
  
      /**
       * @notice Enable fixed APY, reward rate if not enabled
       * @notice Accessable to only owner
       * @param _apyPercent        APY percentage in wei
       * @param _rewardRate        Reward rate per token per day in wei
       */
      function enableFixed(uint256 _apyPercent, uint256 _rewardRate) external onlyOwner {
          require(rewardRate > 0, "StakingDrift: reward rate per token per day required for fixed APY");
          stakeInfo.fixedAPY = _apyPercent;
          stakeInfo.fixedRewardRate = true;
          rewardRate = _rewardRate;
          emit EnableFixed(block.timestamp);
      }
  
      /**
       * @notice Stake with permit
       * @param _amount       Amount in wei
       * @param deadline      Future deadline in timestamp 
       * @param v             secp256k1 signature from `owner`
       * @param r             secp256k1 signature from `owner`
       * @param s             secp256k1 signature from `owner`
       */
      function stakeWithPermit(
          uint256 _amount,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external isOpen checkAmount(_amount) nonReentrant {
          driftToken.permit(_msgSender(), address(this), _amount, deadline, v, r, s);
          revert("StakingDrift: Stake not allowed for this pool");
          // _stake(_amount, _msgSender());
          // updateReward(_msgSender());
      }
  
      /**
       * @notice Stake on behalf. Owner of tokens stakes for someone.
       * @param _amount           Amount in wei
       * @param _userAddress      Address of user to stake for
       */
      function stakeOnBehalf(uint256 _amount, address _userAddress)
          external
          isOpen
          checkAmount(_amount)
          nonReentrant
      {
          require(behalfStakeAllowedAddress[_msgSender()] == true || owner() == _msgSender(), "StakingDrift: Stake not allowed");
          _stake(_amount, _userAddress, _msgSender());
          updateReward(_userAddress);
      }
  
      /**
       * @notice Stake Drift tokens.
       * @param _amount       Amount in wei.
       */
      function stake(uint256 _amount) external isOpen checkAmount(_amount) nonReentrant {
          revert("StakingDrift: Stake not allowed for this pool");
          // _stake(_amount, _msgSender());
          // updateReward(_msgSender());
      }
  
      /**
       * @notice Unstake tokens with rewards if available.
       * @param _amount       Amount in wei.
       */
      function unstake(uint256 _amount) external nonReentrant {
          require(_amount > 0, "StakingDrift: amount must be greater than zero");
          updateReward(_msgSender());
  
          User storage $ = users[_msgSender()];
  
          require($.stakedBalance >= _amount, "StakingDrift: insufficient staked balance");
  
          if(stakeInfo.lockedStake > 0) {
              require(_getDays($.lastStakedTime, block.timestamp) >= stakeInfo.lockedStake, "StakingDrift: your staked token");
          }
  
          uint256 _unstakeFee = _getDays($.lastStakedTime, block.timestamp) < stakeInfo.minStakePeriod
              ? (_amount * unstakeFee) / 10**5
              : 0;
          uint256 amountAfterFee = _amount - _unstakeFee;
          $.stakedBalance -= _amount;
          totalStaked -= _amount;
  
          distributeReward(_msgSender());
  
          // Burn stDRIFT tokens
          stDriftToken.burnFrom(_msgSender(), _amount);
          if (_unstakeFee > 0) {
              driftToken.transfer(feeWallet, _unstakeFee);
          }
          driftToken.transfer(_msgSender(), amountAfterFee);
      }
  
      /**
       * @notice Claim rewards of staked tokens if available.
       */
      function claimRewards() external nonReentrant {
          updateReward(_msgSender()); // Update rewards for the user
          User storage $ = users[_msgSender()];
          require($.rewardsBalance > 0, "StakingDrift: no rewards to claim");
          distributeReward(_msgSender());
      }
  
      /**
       * @notice Add liquidity or Rewards in pool.
       * @notice Accessable to only owner
       * @param amount           Amount in wei.
       */
      function addLiquidty(uint256 amount) external payable onlyOwner {
          require(amount > 0, "StakingDrift: Amount must be greater than zero");
          if (isNative) {
              require(msg.value == amount, "StakingDrift: incorrect amount sent");
          } else {
              rewardToken.transferFrom(_msgSender(), address(this), amount);
          }
          calculateRewardRate();
      }
  
      /**
       * @notice Start stake if not already open.
       * @notice Accessable to only owner
       * @param _stakeEndDeadlineInDays       Stake pool deadline in days or zero otherwise for infinite
       * @param _unstakeFeeRate               Unstake fees percentage multiplied by 1000
       */
      function startStake(uint256 _stakeEndDeadlineInDays, uint256 _unstakeFeeRate)
          external
          onlyOwner
      {
          require(isOpenStake() == false, "StakingDrift : stake is already open.");
          openStake = true;
          stakeStartTimestamp = block.timestamp;
          stakeEndDeadline = _stakeEndDeadlineInDays > 0 ? block.timestamp + (_stakeEndDeadlineInDays * 1 days) : 0;
          unstakeFee = _unstakeFeeRate;
          calculateRewardRate();
          emit StakeOpen(_stakeEndDeadlineInDays, _unstakeFeeRate, block.timestamp);
      }
  
      /**
       * @notice Stop stake if not already closed.
       * @notice Accessable to only owner
       */
      function stopStake() external onlyOwner {
          require(isOpenStake() == true, "StakingDrift : stake is already closed.");
          openStake = false;
          stakeEndDeadline = block.timestamp;
          emit StakeClosed(block.timestamp);
      }
  
      /**
       * @notice Update unstake fees percentage and/or locked days of stake
       * @notice Accessable to only owner
       * @param _newUnstakeFeeRate        Unstake fees percentage multiplied by 1000
       * @param _lockedStakeDays          Locked stake in days if enable locked pool
       */
      function updateUnstakeFeesAndLockedDays(uint256 _newUnstakeFeeRate, uint256 _lockedStakeDays) external onlyOwner {
          unstakeFee = _newUnstakeFeeRate;
          stakeInfo.lockedStake = _lockedStakeDays;
          emit UpdateUnstakeFeesAndLockedDays(_newUnstakeFeeRate, _lockedStakeDays);
      }
  
      /**
       * @notice Update limits and enable or disable feature that users stake (max / min) tokens and minimum stake period
       * @notice Accessable to only owner
       * @param _newMinimumStake      Amount of token user min hold.
       * @param _newMaximumStake      Amount of token user max hold.
       * @param _newMinStakePeriod    Minimum stake period in days
       */
      function updateLimits(
          uint256 _newMinimumStake,
          uint256 _newMaximumStake,
          uint256 _newMinStakePeriod
      ) external onlyOwner {
          stakeInfo.minimumStake = _newMinimumStake;
          stakeInfo.maximumStake = _newMaximumStake;
          stakeInfo.minStakePeriod = _newMinStakePeriod;
          emit UpdateLimits(_newMinimumStake, _newMaximumStake, _newMinStakePeriod);
      }
  
      /**
       * @return {APY}
       */
      function calculateAPY() external view returns (uint256) {
          return _calculateAPY();
      }
  
      /**
       * @return stakedBalance        Staked balance
       * @return lastStakedTime       Last staked timestamp
       * @return lastUpdateTime       Last updated timestamp
       * @return rewardsBalance       Reward balance
       */
      function getUserInfo(address _userAddress)
          external
          view
          returns (
              uint256 stakedBalance,
              uint256 lastStakedTime,
              uint256 lastUpdateTime,
              uint256 rewardsBalance
          )
      {
          User storage user = users[_userAddress];
          (uint256 _pendingRewards, ) = getPendingRewards(_userAddress);
          return (
              user.stakedBalance,
              stakeStartTimestamp,
              user.lastUpdateTime > stakeStartTimestamp ? user.lastUpdateTime : stakeStartTimestamp,
              _pendingRewards
          );
      }    
  
      /**
       * @notice Withdraw reward liquidity from contract.
       * @notice Accessable to only owner
       * @param _amount     Amount in wei
       */
      function withdrawLiquidity(uint256 _amount) public onlyOwner {
          if (isNative) {
              require(
                  address(this).balance >= _amount,
                  "StakingDrift: amount is greater than balance"
              );
              (bool os, ) = payable(owner()).call{value: address(this).balance}("");
              require(os);
          } else {
              require(
                  rewardToken.balanceOf(address(this)) >= _amount,
                  "StakingDrift: amount is greater than balance"
              );
              rewardToken.transfer(owner(), _amount);
          }
      }
  
      /**
       * @notice Withdraw Native Gas Token from contract.
       * @notice Accessable to only owner
       */
      function withdrawNativeFunds() public onlyOwner {
          if (address(this).balance > 0) {
              (bool os, ) = payable(owner()).call{value: address(this).balance}("");
              require(os);
          } else {
              revert("StakingDrift: no funds");
          }
      }
  
      /**
       * @notice Withdraw token from contract.
       * @notice Accessable to only owner
       * @param _tokenAddress     Token address
       */
      function withdrawOtherTokenFunds(address _tokenAddress) public onlyOwner {
          if (IERC20(_tokenAddress).balanceOf(address(this)) > 0) {
              IERC20(_tokenAddress).transfer(owner(), IERC20(_tokenAddress).balanceOf(address(this)));
          } else {
              revert("StakingDrift: no funds");
          }
      }
  
      /**
       * @return {true if pool open, false otherwise}
       */
      function isOpenStake() public view returns (bool) {
          return openStake == true && (block.timestamp < stakeEndDeadline || stakeEndDeadline == 0);
      }
  
      /**
       * @notice Helper function to get pending rewards
       * @param _userAddress          Address of staker
       * @return pendingRewards       Claimable pending rewards
       * @return rewardDays           Days of Unclaimable rewards
       */
      function getPendingRewards(address _userAddress) public view returns (uint256 pendingRewards, uint256 rewardDays) {
          User storage $ = users[_userAddress];
          rewardDays = rewardAccumulateStartTimestamp > 0 ? _getDays(
              $.lastUpdateTime > rewardAccumulateStartTimestamp ? $.lastUpdateTime : rewardAccumulateStartTimestamp,
              stakeEndDeadline > 0 ? min(block.timestamp, stakeEndDeadline) : block.timestamp
          ) : 0;
          uint256 pendingRewardOnSubscribedRewardRateOneDay = 0;
          uint256 pendingRewardsRemains = 0;
          if (rewardDays > 0) {
              pendingRewardOnSubscribedRewardRateOneDay = $.stakedBalance.mul($.rewardRate).div(
                  10**(stakeInfo.fixedRewardRate == true ? 18 : 20)
              );
              pendingRewardsRemains = $.stakedBalance.mul(rewardRate.mul(rewardDays - 1)).div(
                  10**(stakeInfo.fixedRewardRate == true ? 18 : 20)
              );
          }
          pendingRewards = $.rewardsBalance.add(pendingRewardsRemains.add(pendingRewardOnSubscribedRewardRateOneDay).mul(BONUS_MULTIPLIER));
      }
  
      /**
       * @return {Total pending rewards}
       */
      function getTotalPendingRewards() public view returns (uint256) {
          if (isNative) {
              return address(this).balance;
          }
          return rewardToken.balanceOf(address(this));
      }
  
      /**
       * @notice Internal callable function when stake tokens
       * @param _amount       Amount in wei.
       * @param _sender       Sender address who stakes.
       */
      function _stake(uint256 _amount, address _sender) internal {
          _stake(_amount, _sender, _sender);
      }
  
      /**
       * @notice Internal callable function when stake tokens
       * @param _amount           Amount in wei.
       * @param _userAddress      Address of user to stake for.
       * @param _sender           Sender address who stakes.
       */
      function _stake(
          uint256 _amount,
          address _userAddress,
          address _sender
      ) internal {
          User storage $ = users[_userAddress];
          driftToken.transferFrom(_sender, address(this), _amount);
          totalStaked += _amount;
          $.stakedBalance += _amount;
          $.lastStakedTime = block.timestamp;
  
          stDriftToken.mint(_userAddress, _amount);
          emit Staked(_userAddress, _amount);
      }
  
      /**
       * @notice Internal callable function when claim reward calls
       * @param _userAddress      Address of user
       */
      function distributeReward(address _userAddress) internal {
          User storage $ = users[_userAddress];
          uint256 amount = $.rewardsBalance;
          if (amount > 0) {
              $.rewardsBalance = 0; // Reset user rewards
              if (isNative) {
                  amount = min(amount, address(this).balance);
                  _transferNative(_userAddress, amount);
              } else {
                  amount = min(calcDriftDigitsToOther(amount), rewardToken.balanceOf(address(this)));
                  rewardToken.transfer(_msgSender(), amount);
              }
              emit Claimed(_userAddress, amount);
          }
      }
  
      /**
       * @notice Internal callable function to transfer Native Gas Token.
       * @param _recipient      Address of recipient
       * @param _amount         Amount in wei
       */
      function _transferNative(address _recipient, uint256 _amount) internal {
          (bool sent, ) = payable(_recipient).call{value: _amount}("");
          require(sent);
      }
  
      /**
       * @notice Internal callable function to update rewards of staker when user stakes or unstakes
       * @param _userAddress          Address of staker
       */
      function updateReward(address _userAddress) internal {
          User storage $ = users[_userAddress];
          calculateRewardRate(); // Calculate dynamic reward rate
          if (totalStaked > 0) {
              ($.rewardsBalance, ) = getPendingRewards(_userAddress); // Accumulate pending rewards
          }
          $.lastUpdateTime = block.timestamp; // Update last update time
          $.rewardRate = rewardRate;
      }
  
      /**
       * @notice Internal callable function to update and calculate reward rate if Fixed Rate not enabled.
       */
      function calculateRewardRate() internal {
          if(stakeInfo.fixedRewardRate == true) {
              return;
          }
          rewardRate = _calculateAPY().div(365 * 10**2);
      }
  
      /**
       * @notice Helper function to convert Drift decimals to other token decimals
       * @param amount        Amount of tokens in Drift decimals
       */
      function calcDriftDigitsToOther(uint256 amount) internal view returns (uint256 calcAmount) {
          calcAmount =
              (amount * 10**(isNative ? 18 : rewardToken.decimals())) /
              10**driftToken.decimals();
          return calcAmount;
      }
  
      /**
       * @notice Helper function to convert other token decimals to Drift decimals
       * @param amount        Amount of tokens in other token decimals
       */
      function calcOtherToDriftDigits(uint256 amount) internal view returns (uint256 calcAmount) {
          calcAmount =
              (amount * 10**driftToken.decimals()) /
              10**(isNative ? 18 : rewardToken.decimals());
          return calcAmount;
      }
  
      /**
       * @notice Helper function to get APY
       * @return {Fixed APY or Calculated if Fixed not enabled}
       */
      function _calculateAPY() internal view returns (uint256) {
          if (getTotalPendingRewards() == 0 || totalStaked == 0) {
              return 0;
          }
  
          if (stakeInfo.fixedAPY > 0) {
              return stakeInfo.fixedAPY * 10**2;
          }
  
          uint256 apy = ((calcOtherToDriftDigits(getTotalPendingRewards()) * 10**20) / totalStaked);
          return apy;
      }
  
      /**
       * @notice Helper function to find the minimum of two values.
       * @param a     Number 1
       * @param b     Number 2
       * @return {Minimum number}
       */
      function min(uint256 a, uint256 b) internal pure returns (uint256) {
          return a < b ? a : b;
      }
  
      /**
       * @notice Helper function to get days
       * @param _timestampBefore      Before time
       * @param _timestampAfter       After time
       * @return {DAYS}
       */
      function _getDays(uint256 _timestampBefore, uint256 _timestampAfter)
          internal
          pure
          returns (uint256)
      {
          if (_timestampBefore >= _timestampAfter || _timestampBefore == 0 || _timestampAfter == 0) {
              return 0;
          }
          uint256 timestamp = _timestampAfter - _timestampBefore;
          uint256 timestampDays = timestamp.div(60).div(60).div(24);
  
          return timestampDays;
      }
      
      mapping(address => bool) public behalfStakeAllowedAddress;
      uint256 public rewardAccumulateStartTimestamp = 0;
  
      function updateRewardAccumulateStartTimestamp(uint256 _newTimestamp) external onlyOwner {
          rewardAccumulateStartTimestamp = _newTimestamp;
      }
  }
  
  contract StakingDrift2 is StakingDrift {
  
      bool initialized_v2 = false;
  
      function initializeV2(
          address claimAddress,
          uint256 _rewardAccumulateStartTimestamp
      ) external {
          require(!initialized_v2, "Already initialized");
  
          behalfStakeAllowedAddress[claimAddress] = true;
          rewardAccumulateStartTimestamp = _rewardAccumulateStartTimestamp;
  
          version = "2";
          initialized_v2 = true;
      }
      
  }