ETH Price: $2,096.15 (-11.12%)

Contract Diff Checker

Contract Name:
StakingDrift2

Contract Source Code:

File 1 of 1 : StakingDrift2

// 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;
    }
    
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):