ETH Price: $1,801.58 (-0.63%)

Transaction Decoder

Block:
19363159 at Mar-04-2024 05:01:59 PM +UTC
Transaction Fee:
0.033052497360289611 ETH $59.55
Gas Used:
199,519 Gas / 165.660901269 Gwei

Emitted Events:

145 NeoTokyoStaker.Claim( recipient=[Sender] 0x516ec25dcb8dd1f2d42430c3b3074d048ab7d288, reward=16668988319747279937, tax=515535721229297317 )
146 BYTES2.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0x516ec25dcb8dd1f2d42430c3b3074d048ab7d288, value=16668988319747279937 )
147 BYTES2.Transfer( from=0x0000000000000000000000000000000000000000, to=0xAe6D7307Fb3d07Ce35a95857D34af19110f052C1, value=515535721229297317 )

Account State Difference:

  Address   Before After State Difference Code
0x516EC25d...48aB7d288
0.216272354416216101 Eth
Nonce: 166
0.18321985705592649 Eth
Nonce: 167
0.033052497360289611
0x67e1eCFA...a0dCa9e16
(beaverbuild)
12.529483778683869965 Eth12.529543634383869965 Eth0.0000598557
0xa19f5264...2820Bea86

Execution Trace

BYTES2.getReward( _to=0x516EC25dcB8dD1F2D42430c3b3074d048aB7d288 )
  • NeoTokyoStaker.claimReward( _recipient=0x516EC25dcB8dD1F2D42430c3b3074d048aB7d288 ) => ( 16668988319747279937, 515535721229297317 )
    File 1 of 2: BYTES2
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "../access/PermitControl.sol";
    import "../interfaces/IByteContract.sol";
    import "../interfaces/IStaker.sol";
    /**
    \tThis error is thrown when a caller attempts to exchange more BYTES than they 
    \thold.
    \t@param amount The amount of BYTES that the caller attempted to exchange.
    */
    error DoNotHaveEnoughOldBytes (
    \tuint256 amount
    );
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A migrated ERC-20 BYTES token contract for the Neo Tokyo ecosystem.
    \t@author Tim Clancy <@_Enoch>
    \tThis contract is meant to serve as an upgraded replacement for the original 
    \tBYTES contract in order to support tunable emissions from the new Neo Tokyo 
    \tstaker. This contract maintains the requisite stubs to function with the rest 
    \tof the Neo Tokyo ecosystem. This contract also maintains the original admin 
    \tfunctions.
    \t@custom:date February 14th, 2023.
    */
    contract BYTES2 is PermitControl, ERC20("BYTES", "BYTES") {
    \t/// The identifier for the right to perform token burns.
    \tbytes32 public constant BURN = keccak256("BURN");
    \t/// The identifier for the right to perform some contract changes.
    \tbytes32 public constant ADMIN = keccak256("ADMIN");
    \t/// The address of the original BYTES 1.0 contract.
    \taddress immutable public BYTES1;
    \t/// The address of the S1 Citizen contract.
    \taddress immutable public S1_CITIZEN;
    \t/// The address of the Neo Tokyo staker contract.
    \taddress public STAKER;
    \t/// The address of the treasury which will receive minted DAO taxes.
    \taddress public TREASURY;
    \t/**
    \t\tThis event is emitted when a caller upgrades their holdings of BYTES 1.0 to 
    \t\tthe new BYTES 2.0 token.
    \t\t@param caller The address of the caller upgrading their BYTES.
    \t\t@param amount The amount of BYTES upgraded.
    \t*/
    \tevent BytesUpgraded (
    \t\taddress indexed caller,
    \t\tuint256 amount
    \t);
    \t/**
    \t\tConstruct a new instance of this BYTES 2.0 contract configured with the 
    \t\tgiven immutable contract addresses.
    \t\t@param _bytes The address of the BYTES 2.0 ERC-20 token contract.
    \t\t@param _s1Citizen The address of the assembled Neo Tokyo S1 Citizen.
    \t\t@param _staker The address of the new BYTES emitting staker.
    \t\t@param _treasury The address of the DAO treasury.
    \t*/
    \tconstructor (
    \t\taddress _bytes,
    \t\taddress _s1Citizen,
    \t\taddress _staker,
    \t\taddress _treasury
    \t) {
    \t\tBYTES1 = _bytes;
    \t\tS1_CITIZEN = _s1Citizen;
    \t\tSTAKER = _staker;
    \t\tTREASURY = _treasury;
    \t}
    \t/**
    \t\tAllow holders of the old BYTES contract to change them for BYTES 2.0; old 
    \t\tBYTES tokens will be burnt.
    \t\t@param _amount The amount of old BYTES tokens to exchange.
    \t*/
    \tfunction upgradeBytes (
    \t\tuint256 _amount
    \t) external {
    \t\tif (IERC20(BYTES1).balanceOf(msg.sender) < _amount) {
    \t\t\trevert DoNotHaveEnoughOldBytes(_amount);
    \t\t}
    \t\t// Burn the original BYTES 1.0 tokens and mint replacement BYTES 2.0.
    \t\tIByteContract(BYTES1).burn(msg.sender, _amount);
    \t\t_mint(msg.sender, _amount);
    \t\t// Emit the upgrade event.
    \t\temit BytesUpgraded(msg.sender, _amount);
    \t}
    \t/**
    \t\tThis function is called by the S1 Citizen contract to emit BYTES to callers 
    \t\tbased on their state from the staker contract.
    \t\t@param _to The reward address to mint BYTES to.
    \t*/
    \tfunction getReward (
    \t\taddress _to
    \t) external {
    \t\t(
    \t\t\tuint256 reward,
    \t\t\tuint256 daoCommision
    \t\t) = IStaker(STAKER).claimReward(_to);
    \t\t// Mint both reward BYTES and the DAO tax to targeted recipients.
    \t\tif (reward > 0) {
    \t\t\t_mint(_to, reward);
    \t\t}
    \t\tif (daoCommision > 0) {
    \t\t\t_mint(TREASURY, daoCommision);
    \t\t}
    \t}
    \t/**
    \t\tPermit authorized callers to burn BYTES from the `_from` address. When 
    \t\tBYTES are burnt, 2/3 of the BYTES burnt are minted to the DAO treasury. This 
    \t\toperation is never expected to overflow given operational bounds on the 
    \t\tamount of BYTES tokens ever allowed to enter circulation.
    \t\t@param _from The address to burn tokens from.
    \t\t@param _amount The amount of tokens to burn.
    \t*/
    \tfunction burn (
    \t\taddress _from,
    \t\tuint256 _amount
    \t) hasValidPermit(UNIVERSAL, BURN) external {
    \t\t_burn(_from, _amount);
    \t\t/*
    \t\t\tWe are aware that this math does not round perfectly for all values of
    \t\t\t`_amount`. We don't care.
    \t\t*/
    \t\tuint256 treasuryShare;
    \t\tunchecked {
    \t\t\ttreasuryShare = _amount * 2 / 3;
    \t\t}
    \t\t_mint(TREASURY, treasuryShare);
    \t}
    \t/**
    \t\tAllow a permitted caller to update the staker contract address.
    \t\t@param _staker The address of the new staker contract.
    \t*/
    \tfunction changeStakingContractAddress (
    \t\taddress _staker
    \t) hasValidPermit(UNIVERSAL, ADMIN) external {
    \t\tSTAKER = _staker;
    \t}
    \t/**
    \t\tAllow a permitted caller to update the treasury address.
    \t\t@param _treasury The address of the new treasury.
    \t*/
    \tfunction changeTreasuryContractAddress (
    \t\taddress _treasury
    \t) hasValidPermit(UNIVERSAL, ADMIN) external {
    \t\tTREASURY = _treasury;
    \t}
    \t/**
    \t\tThis function is called by the S1 Citizen contract before an NFT transfer 
    \t\tand before a call to `getReward`. For historical reasons it must be left 
    \t\there as a stub and cannot be entirely removed, though now it remains as a 
    \t\tno-op.
    \t\t@custom:param A throw-away parameter to fulfill the Citizen call.
    \t\t@custom:param A throw-away parameter to fulfill the Citizen call.
    \t\t@custom:param A throw-away parameter to fulfill the Citizen call.
    \t*/
    \tfunction updateReward (
    \t\taddress,
    \t\taddress,
    \t\tuint256
    \t) external {
    \t}
    \t/**
    \t\tThis function is called by the S1 Citizen contract when a new citizen is 
    \t\tminted. For historical reasons it must be left here as a stub and cannot be 
    \t\tentirely removed, though now it remains as a no-op.
    \t\t@custom:param A throw-away parameter to fulfill the Citizen call.
    \t\t@custom:param A throw-away parameter to fulfill the Citizen call.
    \t*/
    \tfunction updateRewardOnMint (
    \t\taddress,
    \t\tuint256
    \t) external {
      }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * The default value of {decimals} is 18. To change this, you should override
     * this function so it returns a different value.
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC20
     * applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the default value returned by this function, unless
         * it's overridden.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address to, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(from, to, amount);
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(address from, address to, uint256 amount) internal virtual {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(from, to, amount);
            uint256 fromBalance = _balances[from];
            require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[from] = fromBalance - amount;
                // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                // decrementing then incrementing.
                _balances[to] += amount;
            }
            emit Transfer(from, to, amount);
            _afterTokenTransfer(from, to, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= amount;
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= amount, "ERC20: insufficient allowance");
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` 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 amount) external returns (bool);
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title An advanced permission-management contract.
    \t@author Tim Clancy <@_Enoch>
    \tThis contract allows for a contract owner to delegate specific rights to
    \texternal addresses. Additionally, these rights can be gated behind certain
    \tsets of circumstances and granted expiration times. This is useful for some
    \tmore finely-grained access control in contracts.
    \tThe owner of this contract is always a fully-permissioned super-administrator.
    \t@custom:date August 23rd, 2021.
    */
    abstract contract PermitControl is Ownable {
    \tusing Address for address;
    \t/// A special reserved constant for representing no rights.
    \tbytes32 public constant ZERO_RIGHT = hex"00000000000000000000000000000000";
    \t/// A special constant specifying the unique, universal-rights circumstance.
    \tbytes32 public constant UNIVERSAL = hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
    \t/**
    \t\tA special constant specifying the unique manager right. This right allows an
    \t\taddress to freely-manipulate the `managedRight` mapping.
    \t*/
    \tbytes32 public constant MANAGER = hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
    \t/**
    \t\tA mapping of per-address permissions to the circumstances, represented as
    \t\tan additional layer of generic bytes32 data, under which the addresses have
    \t\tvarious permits. A permit in this sense is represented by a per-circumstance
    \t\tmapping which couples some right, represented as a generic bytes32, to an
    \t\texpiration time wherein the right may no longer be exercised. An expiration
    \t\ttime of 0 indicates that there is in fact no permit for the specified
    \t\taddress to exercise the specified right under the specified circumstance.
    \t\t@dev Universal rights MUST be stored under the 0xFFFFFFFFFFFFFFFFFFFFFFFF...
    \t\tmax-integer circumstance. Perpetual rights may be given an expiry time of
    \t\tmax-integer.
    \t*/
    \tmapping ( address => mapping( bytes32 => mapping( bytes32 => uint256 ))) 
    \t\tpublic permissions;
    \t/**
    \t\tAn additional mapping of managed rights to manager rights. This mapping
    \t\trepresents the administrator relationship that various rights have with one
    \t\tanother. An address with a manager right may freely set permits for that
    \t\tmanager right's managed rights. Each right may be managed by only one other
    \t\tright.
    \t*/
    \tmapping ( bytes32 => bytes32 ) public managerRight;
    \t/**
    \t\tAn event emitted when an address has a permit updated. This event captures,
    \t\tthrough its various parameter combinations, the cases of granting a permit,
    \t\tupdating the expiration time of a permit, or revoking a permit.
    \t\t@param updater The address which has updated the permit.
    \t\t@param updatee The address whose permit was updated.
    \t\t@param circumstance The circumstance wherein the permit was updated.
    \t\t@param role The role which was updated.
    \t\t@param expirationTime The time when the permit expires.
    \t*/
    \tevent PermitUpdated (
    \t\taddress indexed updater,
    \t\taddress indexed updatee,
    \t\tbytes32 circumstance,
    \t\tbytes32 indexed role,
    \t\tuint256 expirationTime
    \t);
    \t/**
    \t\tAn event emitted when a management relationship in `managerRight` is
    \t\tupdated. This event captures adding and revoking management permissions via
    \t\tobserving the update history of the `managerRight` value.
    \t\t@param manager The address of the manager performing this update.
    \t\t@param managedRight The right which had its manager updated.
    \t\t@param managerRight The new manager right which was updated to.
    \t*/
    \tevent ManagementUpdated (
    \t\taddress indexed manager,
    \t\tbytes32 indexed managedRight,
    \t\tbytes32 indexed managerRight
    \t);
    \t/**
    \t\tA modifier which allows only the super-administrative owner or addresses
    \t\twith a specified valid right to perform a call.
    \t\t@param _circumstance The circumstance under which to check for the validity
    \t\t\tof the specified `right`.
    \t\t@param _right The right to validate for the calling address. It must be
    \t\t\tnon-expired and exist within the specified `_circumstance`.
    \t*/
    \tmodifier hasValidPermit (
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) {
    \t\trequire(
    \t\t\t_msgSender() == owner() || hasRight(_msgSender(), _circumstance, _right),
    \t\t\t"P1"
    \t\t);
    \t\t_;
    \t}
    \t/**
    \t\tSet the `_managerRight` whose `UNIVERSAL` holders may freely manage the
    \t\tspecified `_managedRight`.
    \t\t@param _managedRight The right which is to have its manager set to
    \t\t\t`_managerRight`.
    \t\t@param _managerRight The right whose `UNIVERSAL` holders may manage
    \t\t\t`_managedRight`.
    \t*/
    \tfunction setManagerRight (
    \t\tbytes32 _managedRight,
    \t\tbytes32 _managerRight
    \t) external virtual hasValidPermit(UNIVERSAL, MANAGER) {
    \t\trequire(_managedRight != ZERO_RIGHT, "P3");
    \t\tmanagerRight[_managedRight] = _managerRight;
    \t\temit ManagementUpdated(_msgSender(), _managedRight, _managerRight);
    \t}
    \t/**
    \t\tSet the permit to a specific address under some circumstances. A permit may
    \t\tonly be set by the super-administrative contract owner or an address holding
    \t\tsome delegated management permit.
    \t\t@param _address The address to assign the specified `_right` to.
    \t\t@param _circumstance The circumstance in which the `_right` is valid.
    \t\t@param _right The specific right to assign.
    \t\t@param _expirationTime The time when the `_right` expires for the provided
    \t\t\t`_circumstance`.
    \t*/
    \tfunction setPermit (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right,
    \t\tuint256 _expirationTime
    \t) public virtual hasValidPermit(UNIVERSAL, managerRight[_right]) {
    \t\trequire(_right != ZERO_RIGHT, "P2");
    \t\tpermissions[_address][_circumstance][_right] = _expirationTime;
    \t\temit PermitUpdated(
    \t\t\t_msgSender(),
    \t\t\t_address,
    \t\t\t_circumstance,
    \t\t\t_right,
    \t\t\t_expirationTime
    \t\t);
    \t}
    \t/**
    \t\tDetermine whether or not an address has some rights under the given
    \t\tcircumstance, and if they do have the right, until when.
    \t\t@param _address The address to check for the specified `_right`.
    \t\t@param _circumstance The circumstance to check the specified `_right` for.
    \t\t@param _right The right to check for validity.
    \t\t@return The timestamp in seconds when the `_right` expires. If the timestamp
    \t\t\tis zero, we can assume that the user never had the right.
    \t*/
    \tfunction hasRightUntil (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) public view returns (uint256) {
    \t\treturn permissions[_address][_circumstance][_right];
    \t}
    \t/**
    \t\tDetermine whether or not an address has some rights under the given
    \t\tcircumstance,
    \t\t@param _address The address to check for the specified `_right`.
    \t\t@param _circumstance The circumstance to check the specified `_right` for.
    \t\t@param _right The right to check for validity.
    \t\t@return true or false, whether user has rights and time is valid.
    \t*/
    \tfunction hasRight (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) public view returns (bool) {
    \t\treturn permissions[_address][_circumstance][_right] > block.timestamp;
    \t}
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A migrated ERC-20 BYTES token contract for the Neo Tokyo ecosystem.
    \t@author Tim Clancy <@_Enoch>
    \tThis is the interface for the BYTES 2.0 contract.
    \t@custom:date February 14th, 2023.
    */
    interface IByteContract {
    \t/**
    \t\tPermit authorized callers to burn BYTES from the `_from` address. When 
    \t\tBYTES are burnt, 2/3 of the BYTES are sent to the DAO treasury. This 
    \t\toperation is never expected to overflow given operational bounds on the 
    \t\tamount of BYTES tokens ever allowed to enter circulation.
    \t\t@param _from The address to burn tokens from.
    \t\t@param _amount The amount of tokens to burn.
    \t*/
    \tfunction burn (
    \t\taddress _from,
    \t\tuint256 _amount
    \t) external;
    \t
    \t/**
    \t\tThis function is called by the S1 Citizen contract to emit BYTES to callers 
    \t\tbased on their state from the staker contract.
    \t\t@param _to The reward address to mint BYTES to.
    \t*/
    \tfunction getReward (
    \t\taddress _to
    \t) external;
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A pool-based staking contract for the Neo Tokyo ecosystem.
    \t@author Tim Clancy <@_Enoch>
    \t@author Rostislav Khlebnikov <@catpic5buck>
    \tThis is the interface for the staker contract.
    \t@custom:date February 14th, 2023.
    */
    interface IStaker {
    \t/**
    \t\tDetermine the reward, based on staking participation at this moment, of a 
    \t\tparticular recipient. Due to a historic web of Neo Tokyo dependencies, 
    \t\trewards are actually minted through the BYTES contract.
    \t\t@param _recipient The recipient to calculate the reward for.
    \t\t@return A tuple containing (the number of tokens due to be minted to 
    \t\t\t`_recipient` as a reward, and the number of tokens that should be minted 
    \t\t\tto the DAO treasury as a DAO tax).
    \t*/
      function claimReward (
    \t\taddress _recipient
    \t) external returns (uint256, uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @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.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @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 {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    

    File 2 of 2: NeoTokyoStaker
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "../access/PermitControl.sol";
    import "../interfaces/IByteContract.sol";
    import "../interfaces/IGenericGetter.sol";
    import "../interfaces/INTStakedToken.sol";
    /**
    \tThrown when attempting to operate on a non-existent Citizen (S1 or S2).
    \t@param citizenId The ID of the caller's specified Citizen.
    */
    error CitizenDoesNotExist (
    \tuint256 citizenId
    );
    /**
    \tThrown when attempting to get a staker's position of an unknowable asset type.
    \t@param assetType The caller's specified asset type.
    */
    error UnknowablePosition (
    \tuint256 assetType
    );
    /**
    \tThrown when an S1 Citizen with a component Vault attempts to stake while 
    \tattaching an optional non-component Vault.
    \t@param componentVaultId The ID of the S1 Citizen's component Vault.
    \t@param noncomponentVaultId The ID of the Vault the caller attempted to stake.
    */
    error CitizenAlreadyHasVault (
    \tuint256 componentVaultId,
    \tuint256 noncomponentVaultId
    );
    /**
    \tThrown when an S1 Citizen attempts to wrongfully claim the Hand bonus.
    \t@param citizenId The ID of the caller's specified S1 Citizen.
    */
    error CitizenIsNotHand (
    \tuint256 citizenId
    );
    /**
    \tThrown when a BYTES stake would exceed the cap of its corresponding Citizen.
    \t@param attemptedAmount The amount that the user is attempting to stake to.
    \t@param cap The staking cap of the Citizen.
    */
    error AmountExceedsCap (
    \tuint256 attemptedAmount,
    \tuint256 cap
    );
    /**
    \tThrown when attempting to stake BYTES into an unowned Citizen.
    \t@param citizenId The token ID of the Citizen involved in the attempted stake.
    \t@param seasonId The season ID of the Citizen, whether S1 or S2.
    */
    error CannotStakeIntoUnownedCitizen (
    \tuint256 citizenId,
    \tuint256 seasonId
    );
    /**
    \tThrown when attempting to stake BYTES into an invalid Citizen season.
    \t@param seasonId The ID of the Citizen season to try staking BYTES into.
    */
    error InvalidSeasonId (
    \tuint256 seasonId
    );
    /**
    \tThrown when attempting to increase a stake in an asset without matching the 
    \texisting timelock of the asset.
    */
    error MismatchedTimelock ();
    /**
    \tThrown during staking or unstaking if an invalid AssetType is specified.
    \t@param assetType The caller's specified asset type.
    */
    error InvalidAssetType (
    \tuint256 assetType
    );
    /**
    \tThrown during staking if attempting to stake into an unconfigured asset pool.
    \t@param assetType The caller's specified asset type.
    */
    error UnconfiguredPool (
    \tuint256 assetType
    );
    /**
    \tThrown during staking if attempting to stake into an asset pool whose rewards 
    \tare not yet active.
    \t@param assetType The caller's specified asset type.
    */
    error InactivePool (
    \tuint256 assetType
    );
    /**
    \tThrown during staking if an invalid timelock option is specified. Each 
    \tAssetType being staked may have independently-configured timelock options.
    \t@param assetType The caller's specified asset type.
    \t@param timelockId The caller's specified timelock ID against `assetType`.
    */
    error InvalidTimelockOption (
    \tuint256 assetType,
    \tuint256 timelockId
    );
    /// Thrown if the caller of a function is not the BYTES contract.
    error CallerIsNotBYTES ();
    /**
    \tThrown when withdrawing an asset fails to clear a timelock.
    \t
    \t@param endTime The time that the staked asset timelock ends.
    */
    error TimelockNotCleared (
    \tuint256 endTime
    );
    /**
    \tThrown when attempting to withdraw an unowned S1 Citizen.
    \t@param citizenId The ID of the S1 Citizen attempted to be withdrawn.
    */
    error CannotWithdrawUnownedS1 (
    \tuint256 citizenId
    );
    /**
    \tThrown when attempting to withdraw an unowned S2 Citizen.
    \t@param citizenId The ID of the S2 Citizen attempted to be withdrawn.
    */
    error CannotWithdrawUnownedS2 (
    \tuint256 citizenId
    );
    /**
    \tThrown if a caller tries to withdraw more LP tokens than they had staked.
    \t@param attemptedWithdraw The amount of LP tokens that the caller attempted to 
    \t\twithdraw.
    \t@param position The amount of LP tokens that the caller has actually staked.
    */
    error NotEnoughLPTokens (
    \tuint256 attemptedWithdraw,
    \tuint256 position
    );
    /// Thrown if attempting to configure the LP token address post-lock.
    error LockedConfigurationOfLP ();
    /// Thrown when specifying invalid reward windows for a pool.
    error RewardWindowTimesMustIncrease ();
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A pool-based staking contract for the Neo Tokyo ecosystem.
    \t@author Tim Clancy <tim-clancy.eth>
    \t@author Rostislav Khlebnikov <catpic5buck.eth>
    \tThis contract allows callers to stake their Neo Tokyo Citizens (both S1 and 
    \tS2) and BYTES for\ttime-locked emission rewards. The staker operates on a 
    \tpoint-based, competitive system where stakers compete for a finite amount of 
    \tdaily emissions. It allows permissioned managers to configure various 
    \temission details for the Neo Tokyo ecosystem.
    \t@custom:date February 14th, 2023.
    */
    contract NeoTokyoStaker is PermitControl, ReentrancyGuard {
    \t/// The `transferFrom` selector for ERC-20 and ERC-721 tokens.
    \tbytes4 constant private _TRANSFER_FROM_SELECTOR = 0x23b872dd;
    \t/// The `transfer` selector for ERC-20 tokens.
    \tbytes4 constant private _TRANSFER_SELECTOR = 0xa9059cbb;
    \t/// A constant multiplier to reduce overflow in staking calculations.
    \tuint256 constant private _PRECISION = 1e12;
    \t/// A constant divisor to calculate points and multipliers as basis points.
    \tuint256 constant private _DIVISOR = 100;
    \t/// The number of BYTES needed to get one point in BYTES staking calculations.
    \tuint256 constant private _BYTES_PER_POINT = 200 * 1e18;
    \t/// The identifier for the right to configure the LP token address.
    \tbytes32 public constant CONFIGURE_LP = keccak256("CONFIGURE_LP");
    \t/// The identifier for the right to configure timelock options.
    \tbytes32 public constant CONFIGURE_TIMELOCKS = keccak256(
    \t\t"CONFIGURE_TIMELOCKS"
    \t);
    \t/// The identifier for the right to configure Identity and Vault points.
    \tbytes32 public constant CONFIGURE_CREDITS = keccak256("CONFIGURE_CREDITS");
    \t/// The identifier for the right to configure emission rates and the DAO tax.
    \tbytes32 public constant CONFIGURE_POOLS = keccak256("CONFIGURE_POOLS");
    \t/// The identifier for the right to configure BYTES staking caps.
    \tbytes32 public constant CONFIGURE_CAPS = keccak256("CONFIGURE_CAPS");
    \t/// The address of the new BYTES 2.0 token contract.
    \taddress immutable public BYTES;
    \t/// The address of the assembled Neo Tokyo S1 Citizen contract.
    \taddress immutable public S1_CITIZEN;
    \t/// The address of the assembled Neo Tokyo S2 Citizen contract.
    \taddress immutable public S2_CITIZEN;
    \t/// The address of the LP token contract.
    \taddress public LP;
    \t/**
    \t\tThe address of the Neo Tokyo S1 Identity contract. This specific contract 
    \t\taddress is stored to check an assembled S1 Citizen's specific component 
    \t\tidentity in order to check for Hands of the Citadel.
    \t*/
    \taddress immutable public IDENTITY;
    \t/// The address of the Neo Tokyo S1 Vault contract.
    \taddress immutable public VAULT;
    \t// The address of the soulbound token that is given to wallets upon staking a citizen
    \t// and burned upon unstaking.    
    \taddress public NT_STAKED_CITIZEN;
    \t/**
    \t\tThe limit on the number of BYTES that may be staked per S1 Citizen 
    \t\tassembled with a component Vault or per Vault-less S1 Citizen staked 
    \t\talongside a Vault.
    \t*/
    \tuint256 public VAULT_CAP;
    \t/**
    \t\tThe limit on the number of BYTES that may be staked per S2 Citizen or S1 
    \t\tCitizen without Vault.
    \t*/
    \tuint256 public NO_VAULT_CAP;
    \t/**
    \t\tThis enum tracks each type of asset that may be operated on with this 
    \t\tstaker.
    \t\t@param S1_CITIZEN A staked Neo Tokyo S1 Citizen.
    \t\t@param S2_CITIZEN A staked Neo Tokyo S2 Citizen.
    \t\t@param BYTES A set of staked BYTES ERC-20 token.
    \t\t@param LP Staked BYTES-ETH LP ERC-20 token.
    \t*/
    \tenum AssetType {
    \t\tS1_CITIZEN,
    \t\tS2_CITIZEN,
    \t\tBYTES,
    \t\tLP
    \t}
    \t/**
    \t\tThis mapping contains the per-asset configuration of different timelock 
    \t\tperiods with their associated multipliers. For each asset, the interior 
    \t\tmapping correlates a particular timelock option to a uint256 which encodes 
    \t\tthe duration of the timelock in its upper 128 bits and the multiplier 
    \t\toffered by that timelock, as basis points, in its lower 128 bits.
    \t*/
    \tmapping ( AssetType => mapping ( uint256 => uint256 )) public\ttimelockOptions;
    \t/**
    \t\tThis struct defines a specific time window in reward emission history. For 
    \t\ta particular asset staking pool, it represents that beginning from 
    \t\t`startTime`, the pool had a per-second reward emission rate of `reward`.
    \t\t@param startTime The time at which the daily reward activated.
    \t\t@param reward The reward emission rate beginning at `startTime`.
    \t*/
    \tstruct RewardWindow {
    \t\tuint128 startTime;
    \t\tuint128 reward;
    \t}
    \t/**
    \t\tThis struct is used to define both the configuration details for a 
    \t\tparticular asset staking pool and the state of that pool as callers begin 
    \t\tinteracting with it.
    \t\t@param totalPoints The total number of points in the pool.
    \t\t@param daoTax The percent, in basis points, of the reward emissions sent to 
    \t\t\tthe DAO.
    \t\t@param rewardPerPoint The cumulative rewards earned for each point in the pool.
    \t\t@param lastUpdated The time at which this pool's points were last updated.
    \t\t@param rewardCount A count of the number of reward windows in the 
    \t\t\t`rewardWindows` mapping, used for iterating.
    \t\t@param rewardWindows A mapping of the historic amount of BYTES token \t
    \t\t\trewarded per-second across all stakers in this particular pool.
    \t*/
    \tstruct PoolData {
    \t\tuint256 totalPoints;
    \t\tuint256 daoTax;
    \t\tuint256 rewardPerPoint;
    \t\tuint256 lastUpdated;
    \t\tuint256 rewardCount;
    \t\tmapping ( uint256 => RewardWindow ) rewardWindows;
    \t\tmapping ( address => uint256 ) rewardsMissed;
    \t}
    \t/// Map an asset type to its corresponding pool data. 
    \tmapping ( AssetType => PoolData ) private _pools;
    \t
    \t/// Track the last time a caller was granted their rewards for each asset.
    \tmapping ( address => mapping ( AssetType => uint256 )) public lastRewardTime;
    \t/// Track the total reward amount earnings accrued to a particular caller.
    \tmapping ( address => mapping ( AssetType => uint256 )) public rewardAccrued;
    \t/** 
    \t\tThis admin-configurable double-mapping allows us to deduce the Identity 
    \t\t"Credit Yield" string of a Neo Tokyo S1 Citizen given the Citizen's reward 
    \t\trate and the reward rate of the Citizen's Vault.
    \t*/
    \tmapping ( uint256 => mapping ( string => string )) public identityCreditYield;
    \t/// Assign a configurable multiplier to each S1 Citizen's Identity credit. 
    \tmapping ( string => uint256 ) public identityCreditPoints;
    \t/// Assign a configurable multiplier to each S1 Citizen's Vault credit. 
    \tmapping ( string => uint256 ) public vaultCreditMultiplier;
    \t/**
    \t\tThis struct records the state of each staked S1 Citizen.
    \t\t@param stakedBytes The number of BYTES that have been staked into this S1 
    \t\t\tCitizen. Depending on the value of `hasVault`, this S1 Citizen  will be 
    \t\t\tbound to either the `VAULT_CAP` limit or `NO_VAULT_CAP` limit on the 
    \t\t\tnumber of BYTES that may be staked.
    \t\t@param timelockEndTime The time at which the forced, timelocked staking of 
    \t\t\tthis S1 Citizen ends. After this time the S1 Citizen may be withdrawn 
    \t\t\tfrom the staker.
    \t\t@param points The number of base points that this staked S1 Citizen is 
    \t\t\tworth, thus determining its share of emissions. An S1 Citizen's base 
    \t\t\tpoints are a function of the S1 Citizen's component Identity and any 
    \t\t\tassociated Vault multiplier. The base points are also multiplied by the 
    \t\t\ttimelock option chosen at the time of staking. The base points are 
    \t\t\tsupplemented in points calculations by the value of `stakedBytes`.
    \t\t@param stakedVaultId The optional ID of the Vault, if there is one, that 
    \t\t\thas been staked alongside this S1 Citizen. If `hasVault` is true, this 
    \t\t\tvalue may be non-zero to indicate a staked but non-component Vault. If 
    \t\t\t`hasVault` is true and this value is zero, that is indicative of an S1 
    \t\t\tCitizen with a component Vault.
    \t\t@param hasVault A boolean indicating whether or not this S1 Citizen has an 
    \t\t\tassociated Vault, whether that Vault is a component Vault assembled into 
    \t\t\tthe S1 Citizen or one that has been staked alongside the S1 Citizen.
    \t*/
    \tstruct StakedS1Citizen {
    \t\tuint256 stakedBytes;
    \t\tuint256 timelockEndTime;
    \t\tuint256 points;
    \t\tuint256 stakedVaultId;
    \t\tbool hasVault;
    \t}
    \t/**
    \t\tA double mapping from a caller address to a specific S1 Citizen token ID to 
    \t\tthe staking status of each S1 Citizen. This records the unique per-user 
    \t\tstaking status of each S1 citizen.
    \t*/
    \tmapping ( address => mapping( uint256 => StakedS1Citizen )) public stakedS1;
    \t/**
    \t\tThis mapping correlates a caller address to a list of their 
    \t\tcurrently-staked S1 Citizen token IDs.
    \t*/
    \tmapping ( address => uint256[] ) private _stakerS1Position;
    \t/**
    \t\tThis struct records the state of each staked S2 Citizen.
    \t\t@param stakedBytes The number of BYTES that have been staked into this S2 
    \t\t\tCitizen.
    \t\t@param timelockEndTime The time at which the forced, timelocked staking of 
    \t\t\tthis S2 Citizen ends. After this time the S2 Citizen may be withdrawn 
    \t\t\tfrom the staker.
    \t\t@param points The number of base points that this staked S2 Citizen is 
    \t\t\tworth, thus determining its share of emissions. An S2 Citizen's base 
    \t\t\tpoints are a function of the timelock option chosen at the time of 
    \t\t\tstaking. The base points are supplemented in points calculations by the 
    \t\t\tvalue of `stakedBytes`.
    \t*/
    \tstruct StakedS2Citizen {
    \t\tuint256 stakedBytes;
    \t\tuint256 timelockEndTime;
    \t\tuint256 points;
    \t}
    \t/**
    \t\tA double mapping from a caller address to a specific S2 Citizen token ID to 
    \t\tthe staking status of each S2 Citizen. This records the unique per-user 
    \t\tstaking status of each S2 citizen.
    \t*/
    \tmapping ( address => mapping( uint256 => StakedS2Citizen )) public stakedS2;
    \t/**
    \t\tThis mapping correlates a caller address to a list of their 
    \t\tcurrently-staked S2 Citizen token IDs.
    \t*/
    \tmapping ( address => uint256[] ) private _stakerS2Position;
    \t/**
    \t\tThis struct defines the LP token staking position of a particular staker.
    \t\t@param amount The amount of LP tokens staked by the staker.
    \t\t@param timelockEndTime The tiume at which the forced, timelocked staking of 
    \t\t\tthese LP tokens ends. After this the LP tokens may be withdrawn.
    \t\t@param points The number of points that this LP position accrues.
    \t\t@param multiplier The multiplier portion of the timelock option recorded so 
    \t\t\tas to enforce later stakes to use the same point rate.
    \t*/
    \tstruct LPPosition {
    \t\tuint256 amount;
    \t\tuint256 timelockEndTime;
    \t\tuint256 points;
    \t\tuint256 multiplier;
    \t}
    \t/**
    \t\tThis mapping correlates each caller address to details regarding the LP 
    \t\ttoken stake of that caller.
    \t*/
    \tmapping ( address => LPPosition ) public stakerLPPosition;
    \t/**
    \t\tThis struct supplies the position output state of each staked S1 Citizen.
    \t\t@param citizenId The token ID of this S1 Citizen.
    \t\t@param stakedBytes The number of BYTES that have been staked into this S1 
    \t\t\tCitizen. Depending on the value of `hasVault`, this S1 Citizen  will be 
    \t\t\tbound to either the `VAULT_CAP` limit or `NO_VAULT_CAP` limit on the 
    \t\t\tnumber of BYTES that may be staked.
    \t\t@param timelockEndTime The time at which the forced, timelocked staking of 
    \t\t\tthis S1 Citizen ends. After this time the S1 Citizen may be withdrawn 
    \t\t\tfrom the staker.
    \t\t@param points The number of base points that this staked S1 Citizen is 
    \t\t\tworth, thus determining its share of emissions. An S1 Citizen's base 
    \t\t\tpoints are a function of the S1 Citizen's component Identity and any 
    \t\t\tassociated Vault multiplier. The base points are also multiplied by the 
    \t\t\ttimelock option chosen at the time of staking. The base points are 
    \t\t\tsupplemented in points calculations by the value of `stakedBytes`.
    \t\t@param stakedVaultId The optional ID of the Vault, if there is one, that 
    \t\t\thas been staked alongside this S1 Citizen. If `hasVault` is true, this 
    \t\t\tvalue may be non-zero to indicate a staked but non-component Vault. If 
    \t\t\t`hasVault` is true and this value is zero, that is indicative of an S1 
    \t\t\tCitizen with a component Vault.
    \t\t@param hasVault A boolean indicating whether or not this S1 Citizen has an 
    \t\t\tassociated Vault, whether that Vault is a component Vault assembled into 
    \t\t\tthe S1 Citizen or one that has been staked alongside the S1 Citizen.
    \t*/
    \tstruct StakedS1CitizenOutput {
    \t\tuint256 citizenId;
    \t\tuint256 stakedBytes;
    \t\tuint256 timelockEndTime;
    \t\tuint256 points;
    \t\tuint256 stakedVaultId;
    \t\tbool hasVault;
    \t}
    \t/**
    \t\tThis struct supplies the position output state of each staked S2 Citizen.
    \t\t@param citizenId The token ID of this S1 Citizen.
    \t\t@param stakedBytes The number of BYTES that have been staked into this S2 
    \t\t\tCitizen.
    \t\t@param timelockEndTime The time at which the forced, timelocked staking of 
    \t\t\tthis S2 Citizen ends. After this time the S2 Citizen may be withdrawn 
    \t\t\tfrom the staker.
    \t\t@param points The number of base points that this staked S2 Citizen is 
    \t\t\tworth, thus determining its share of emissions. An S2 Citizen's base 
    \t\t\tpoints are a function of the timelock option chosen at the time of 
    \t\t\tstaking. The base points are supplemented in points calculations by the 
    \t\t\tvalue of `stakedBytes`.
    \t*/
    \tstruct StakedS2CitizenOutput {
    \t\tuint256 citizenId;
    \t\tuint256 stakedBytes;
    \t\tuint256 timelockEndTime;
    \t\tuint256 points;
    \t}
    \t/**
    \t\tThis struct records the state of all assets staked by a particular staker 
    \t\taddress in this staker.
    \t\t@param stakedS1Citizens An array containing information about each S1 
    \t\t\tCitizen staked by a particular staker address.
    \t\t@param stakedS2Citizens An array containing information about each S2 
    \t\t\tCitizen staked by a particular staker address.
    \t\t@param stakedLPPosition Details regarding the LP token stake of a particular
    \t\t\tstaker address.
    \t*/
    \tstruct StakerPosition {
    \t\tStakedS1CitizenOutput[] stakedS1Citizens;
    \t\tStakedS2CitizenOutput[] stakedS2Citizens;
    \t\tLPPosition stakedLPPosition;
    \t}
    \t/// Whether or not setting the LP token contract address is locked.
    \tbool public lpLocked;
    \t/**
    \t\tThis struct records an input to the staker's `configurePools` function.
    \t\t@param assetType The asset type for the corresponding pool to set.
    \t\t@param daoTax The percent, in basis points, of the reward emissions sent to 
    \t\t\tthe DAO.
    \t\t@param rewardWindows An array specifying the historic amount of BYTES token
    \t\t\trewarded per-second across all stakers in this particular pool.
    \t*/
    \tstruct PoolConfigurationInput {
    \t\tAssetType assetType;
    \t\tuint256 daoTax;
    \t\tRewardWindow[] rewardWindows;
    \t}
    \t/**
    \t\tThis event is emitted when an asset is successfully staked.
    \t\t@param staker The address of the caller who staked an `asset`.
    \t\t@param asset The address of the asset being staked.
    \t\t@param timelockOption Data encoding the parameters surrounding the timelock 
    \t\t\toption used in staking the particular asset. Alternatively, this encodes 
    \t\t\tctiizen information for BYTES staking.
    \t\t@param amountOrTokenId The amount of `asset` staked or, for S1 and S2 
    \t\t\tCitizens, the ID of the specific token staked.
    \t*/
    \tevent Stake (
    \t\taddress indexed staker,
    \t\taddress indexed asset,
    \t\tuint256 timelockOption,
    \t\tuint256 amountOrTokenId
    \t);
    \t/**
    \t\tThis event is emitted each time a recipient claims a reward.
    \t\t@param recipient The recipient of the reward.
    \t\t@param reward The amount of BYTES rewarded to the `recipient`.
    \t\t@param tax The amount of BYTES minted as tax to the DAO.
    \t*/
    \tevent Claim (
    \t\taddress indexed recipient,
    \t\tuint256 reward,
    \t\tuint256 tax
    \t);
    \t/**
    \t\tThis event is emitted when an asset is successfully withdrawn.
    \t\t@param caller The address of the caller who withdrew an `asset`.
    \t\t@param asset The address of the asset being withdrawn.
    \t\t@param amountOrTokenId The amount of `asset` withdrawn or, for S1 and S2 
    \t\t\tCitizens, the ID of the specific token withdrawn.
    \t*/
    \tevent Withdraw (
    \t\taddress indexed caller,
    \t\taddress indexed asset,
    \t\tuint256 amountOrTokenId
    \t);
    \t/**
    \t\tConstruct a new instance of this Neo Tokyo staker configured with the given 
    \t\timmutable contract addresses.
    \t\t@param _bytes The address of the BYTES 2.0 ERC-20 token contract.
    \t\t@param _s1Citizen The address of the assembled Neo Tokyo S1 Citizen.
    \t\t@param _s2Citizen The address of the assembled Neo Tokyo S2 Citizen.
    \t\t@param _lpToken The address of the LP token.
    \t\t@param _identity The address of the specific Neo Tokyo Identity sub-item.
    \t\t@param _vault The address of the specific Neo Tokyo Vault sub-item.
    \t\t@param _vaultCap The limit on the number of BYTES that may be staked per S1 
    \t\t\tCitizen assembled with a component Vault or staked alongside a Vault.
    \t\t@param _noVaultCap The limit on the number of BYTES that may be staked per 
    \t\t\tS2 Citizen or S1 Citizen without Vault.
    \t*/
    \tconstructor (
    \t\taddress _bytes,
    \t\taddress _s1Citizen,
    \t\taddress _s2Citizen,
    \t\taddress _lpToken,
    \t\taddress _identity,
    \t\taddress _vault,
    \t\taddress _sbt,
    \t\tuint256 _vaultCap,
    \t\tuint256 _noVaultCap
    \t) {
    \t\tBYTES = _bytes;
    \t\tS1_CITIZEN = _s1Citizen;
    \t\tS2_CITIZEN = _s2Citizen;
    \t\tLP = _lpToken;
    \t\tIDENTITY = _identity;
    \t\tVAULT = _vault;
    \t\tNT_STAKED_CITIZEN = _sbt;
    \t\tVAULT_CAP = _vaultCap;
    \t\tNO_VAULT_CAP = _noVaultCap;
    \t}
    \t/**
    \t\tNeo Tokyo Identity items do not expose their "Credit Yield" trait values in 
    \t\tan easily-consumed fashion. This function works backwards to calculate the 
    \t\tunderlying "Credit Yield" trait value of the component Identity item of the 
    \t\tNeo Tokyo S1 Citizen with the token ID of `_citizenId` given the reward 
    \t\trate of the S1 Citizen as a whole and the credit multiplier of any 
    \t\tcomponent Vault.
    \t\t@param _citizenId The token ID of the Neo Tokyo S1 Citizen to retrieve an 
    \t\t\tIdentity "Credit Yield" trait value for.
    \t\t@param _vaultId The token ID of the Neo Tokyo S1 Citizen's component Vault, 
    \t\t\tif there is one. This parameter is separated to optimized for callers who 
    \t\t\thave already predetermined the token ID of the Vault.
    \t\t@return The "Credit Yield" trait value of the component Identity item of 
    \t\t\tthe S1 Citizen with the token ID of `_citizenId`.
    \t*/
    \tfunction getCreditYield (
    \t\tuint256 _citizenId,
    \t\tuint256 _vaultId
    \t) public view returns (string memory) {
    \t\t// Retrieve the total reward rate of this S1 Citizen.
    \t\tIGenericGetter citizen = IGenericGetter(S1_CITIZEN);
    \t\tuint256 rewardRate = citizen.getRewardRateOfTokenId(_citizenId);
    \t\tif (rewardRate == 0) {
    \t\t\trevert CitizenDoesNotExist(_citizenId);
    \t\t}
    \t\t// Retrieve the credit rate multiplier of any associated Vault.
    \t\tIGenericGetter vault = IGenericGetter(VAULT);
    \t\tstring memory vaultMultiplier = (_vaultId != 0)
    \t\t\t? vault.getCreditMultiplier(_vaultId)
    \t\t\t: "";
    \t\t
    \t\t// Deduce the original Identity credit yield.
    \t\treturn identityCreditYield[rewardRate][vaultMultiplier];
    \t}
    \t/**
    \t\tThe multipliers to S1 Citizen points contributed by their component Vaults 
    \t\tmay be independently configured by permitted administrators of this staking 
    \t\tcontract. This helper function returns any of the multipliers that may have 
    \t\tbeen configured.
    \t\t@param _vaultId The token ID of a Neo Tokyo S1 Vault to retrieve the 
    \t\t\tconfigued multiplier for.
    \t\t@return The configured point multiplier for the Vault with token ID of 
    \t\t\t`_vaultId`.
    \t*/
    \tfunction getConfiguredVaultMultiplier (
    \t\tuint256 _vaultId
    \t) public view returns (uint256) {
    \t\t// Retrieve the credit rate multiplier of the Vault.
    \t\tIGenericGetter vault = IGenericGetter(VAULT);
    \t\tstring memory vaultMultiplier = (_vaultId != 0)
    \t\t\t? vault.getCreditMultiplier(_vaultId)
    \t\t\t: "";
    \t\t
    \t\t// Deduce the configured Vault multiplier.
    \t\treturn vaultCreditMultiplier[vaultMultiplier];
    \t}
    \t/**
    \t\tReturn the list of `_staker`'s token IDs for the specified `_assetType` if 
    \t\tthat type is the Neo Tokyo S1 Citizen or S2 Citizen. In order to determine 
    \t\tthe staker's position in the LP asset type, the public `stakerLPPosition` 
    \t\tmapping should be used. It is not valid to directly determine the position 
    \t\tin BYTES of a particular staker; to retrieve that kind of cumulative data 
    \t\tthe full output `getStakerPositions` function should be used.
    \t\t@param _staker The address of the staker to check for staked Citizen 
    \t\t\tholdings.
    \t\t@param _assetType The asset type to check for staked holdings. This must be 
    \t\t\tthe S1 Citizen or S2 Citizen type.
    \t\t@return The list of token IDs of a particular Citizen type that have been 
    \t\t\tstaked by `_staker`.
    \t*/
    \tfunction getStakerPosition (
    \t\taddress _staker,
    \t\tAssetType _assetType
    \t) external view returns (uint256[] memory) {
    \t\tif (_assetType == AssetType.S1_CITIZEN) {
    \t\t\treturn _stakerS1Position[_staker];
    \t\t} else if (_assetType == AssetType.S2_CITIZEN) {
    \t\t\treturn _stakerS2Position[_staker];
    \t\t} else {
    \t\t\trevert UnknowablePosition(uint256(_assetType));
    \t\t}
    \t}
    \t/**
    \t\tRetrieve the entire position of the specified `_staker` across all asset 
    \t\ttypes in this staker.
    \t\t@param _staker The address of the staker to check for assets.
    \t\t@return The position of the `_staker` across all asset types.
    \t*/
    \tfunction getStakerPositions (
    \t\taddress _staker
    \t) external view returns (StakerPosition memory) {
    \t\t// Compile the S1 Citizen details.
    \t\tStakedS1CitizenOutput[] memory stakedS1Details =
    \t\t\tnew StakedS1CitizenOutput[](_stakerS1Position[_staker].length);
    \t\tfor (uint256 i; i < _stakerS1Position[_staker].length; ) {
    \t\t\tuint256 citizenId = _stakerS1Position[_staker][i];
    \t\t\tStakedS1Citizen memory citizenDetails = stakedS1[_staker][citizenId];
    \t\t\tstakedS1Details[i] = StakedS1CitizenOutput({
    \t\t\t\tcitizenId: citizenId,
    \t\t\t\tstakedBytes: citizenDetails.stakedBytes,
    \t\t\t\ttimelockEndTime: citizenDetails.timelockEndTime,
    \t\t\t\tpoints: citizenDetails.points,
    \t\t\t\thasVault: citizenDetails.hasVault,
    \t\t\t\tstakedVaultId: citizenDetails.stakedVaultId
    \t\t\t});
    \t\t\tunchecked { i++; }
    \t\t}
    \t\t// Compile the S2 Citizen details.
    \t\tStakedS2CitizenOutput[] memory stakedS2Details =
    \t\t\tnew StakedS2CitizenOutput[](_stakerS2Position[_staker].length);
    \t\tfor (uint256 i; i < _stakerS2Position[_staker].length; ) {
    \t\t\tuint256 citizenId = _stakerS2Position[_staker][i];
    \t\t\tStakedS2Citizen memory citizenDetails = stakedS2[_staker][citizenId];
    \t\t\tstakedS2Details[i] = StakedS2CitizenOutput({
    \t\t\t\tcitizenId: citizenId,
    \t\t\t\tstakedBytes: citizenDetails.stakedBytes,
    \t\t\t\ttimelockEndTime: citizenDetails.timelockEndTime,
    \t\t\t\tpoints: citizenDetails.points
    \t\t\t});
    \t\t\tunchecked { i++; }
    \t\t}
    \t\t// Return the final output position struct.
    \t\treturn StakerPosition({
    \t\t\tstakedS1Citizens: stakedS1Details,
    \t\t\tstakedS2Citizens: stakedS2Details,
    \t\t\tstakedLPPosition: stakerLPPosition[_staker]
    \t\t});
    \t}
    \t/**
    \t\tA private helper function for performing the low-level call to 
    \t\t`transferFrom` on either a specific ERC-721 token or some amount of ERC-20 
    \t\ttokens.
    \t\t@param _asset The address of the asset to perform the transfer call on.
    \t\t@param _from The address to attempt to transfer the asset from.
    \t\t@param _to The address to attempt to transfer the asset to.
    \t\t@param _idOrAmount This parameter encodes either an ERC-721 token ID or an 
    \t\t\tamount of ERC-20 tokens to attempt to transfer, depending on what 
    \t\t\tinterface is implemented by `_asset`.
    \t*/
    \tfunction _assetTransferFrom (
    \t\taddress _asset,
    \t\taddress _from,
    \t\taddress _to,
    \t\tuint256 _idOrAmount
    \t) private {
    \t\t(bool success, bytes memory data) = 
    \t\t\t_asset.call(
    \t\t\t\tabi.encodeWithSelector(
    \t\t\t\t\t_TRANSFER_FROM_SELECTOR,
    \t\t\t\t\t_from,
    \t\t\t\t\t_to, 
    \t\t\t\t\t_idOrAmount
    \t\t\t\t)
    \t\t\t);
    \t\t// Revert if the low-level call fails.
    \t\tif (!success) {
    \t\t\trevert(string(data));
    \t\t}
    \t}
    \t/**
    \t\tA private helper function for performing the low-level call to `transfer` 
    \t\ton some amount of ERC-20 tokens.
    \t\t@param _asset The address of the asset to perform the transfer call on.
    \t\t@param _to The address to attempt to transfer the asset to.
    \t\t@param _amount The amount of ERC-20 tokens to attempt to transfer.
    \t*/
    \tfunction _assetTransfer (
    \t\taddress _asset,
    \t\taddress _to,
    \t\tuint256 _amount
    \t) private {
    \t\t(bool success, bytes memory data) = 
    \t\t\t_asset.call(
    \t\t\t\tabi.encodeWithSelector(
    \t\t\t\t\t_TRANSFER_SELECTOR,
    \t\t\t\t\t_to, 
    \t\t\t\t\t_amount
    \t\t\t\t)
    \t\t\t);
    \t\t// Revert if the low-level call fails.
    \t\tif (!success) {
    \t\t\trevert(string(data));
    \t\t}
    \t}
    \t/**
    \t\tA private helper for checking equality between two strings.
    \t\t@param _a The first string to compare.
    \t\t@param _b The second string to compare.
    \t\t@return Whether or not `_a` and `_b` are equal.
    \t*/
    \tfunction _stringEquals (
    \t\tstring memory _a,
    \t\tstring memory _b
    \t) private pure returns (bool) {
    \t\tbytes memory a = bytes(_a);
    \t\tbytes memory b = bytes(_b);
    \t\t
    \t\t// Check equivalence of the two strings by comparing their contents.
    \t\tbool equal = true;
    \t\tassembly {
    \t\t\tlet length := mload(a)
    \t\t\tswitch eq(length, mload(b))
    \t\t\t// Proceed to compare string contents if lengths are equal. 
    \t\t\tcase 1 {
    \t\t\t\tlet cb := 1
    \t\t\t\t// Iterate through the strings and compare contents.
    \t\t\t\tlet mc := add(a, 0x20)
    \t\t\t\tlet end := add(mc, length)
    \t\t\t\tfor {
    \t\t\t\t\tlet cc := add(b, 0x20)
    \t\t\t\t} eq(add(lt(mc, end), cb), 2) {
    \t\t\t\t\tmc := add(mc, 0x20)
    \t\t\t\t\tcc := add(cc, 0x20)
    \t\t\t\t} {
    \t\t\t\t\t// If any of these checks fails then arrays are not equal.
    \t\t\t\t\tif iszero(eq(mload(mc), mload(cc))) {
    \t\t\t\t\t\tequal := 0
    \t\t\t\t\t\tcb := 0
    \t\t\t\t\t}
    \t\t\t\t}
    \t\t\t}
    \t\t\t// By default the array length is not equal so the strings are not equal.
    \t\t\tdefault {
    \t\t\t\tequal := 0
    \t\t\t}
    \t\t}
    \t\treturn equal;
    \t}
    \t/**
    \t\tA private helper function for managing the staking of a particular S1 
    \t\tCitizen. S1 Citizens may optionally be staked at the same time as a Vault, 
    \t\tif they do not already contain a Vault.
    \t\t@param _timelock The selected timelock option for the asset being staked. 
    \t\t\tThis encodes the timelock duration and multiplier.
    \t*/
    \tfunction _stakeS1Citizen (
    \t\tuint256 _timelock
    \t) private {
    \t\tuint256 citizenId;
    \t\tuint256 vaultId;
    \t\tuint256 handClaimant;
    \t\t/*
    \t\t\tExtract the S1 Citizen ID, optional Vault token ID, and optional Hand 
    \t\t\tclaimant ID from calldata.
    \t\t*/
    \t\tassembly {
    \t\t\tcitizenId := calldataload(0x44)
    \t\t\tvaultId := calldataload(0x64)
    \t\t\thandClaimant := calldataload(0x84)
    \t\t}
    \t\t/*
    \t\t\tAttempt to transfer the S1 Citizen to be held in escrow by this staking 
    \t\t\tcontract. This transfer will fail if the caller is not the holder of the 
    \t\t\tCitizen. This prevents double staking.
    \t\t*/
    \t\t_assetTransferFrom(S1_CITIZEN, msg.sender, address(this), citizenId);
    \t\t// Retrieve storage for tracking the staking state of this S1 Citizen.
    \t\tStakedS1Citizen storage citizenStatus = stakedS1[msg.sender][citizenId];
    \t\t// Attach a getter to the S1 Citizen and check for a component Vault.
    \t\tIGenericGetter citizen = IGenericGetter(S1_CITIZEN);
    \t\tuint256 citizenVaultId = citizen.getVaultIdOfTokenId(citizenId);
    \t\t/*
    \t\t\tA new Vault to stake may only be provided if the S1 Citizen being staked 
    \t\t\tdoes not already have a component Vault.
    \t\t*/
    \t\tif (citizenVaultId != 0 && vaultId != 0) {
    \t\t\trevert CitizenAlreadyHasVault(citizenVaultId, vaultId);
    \t\t/*
    \t\t\tIf no optional vault is provided, and the S1 Citizen being staked already 
    \t\t\thas an existing Vault, override the provided `vaultId`.
    \t\t*/
    \t\t} else if (citizenVaultId != 0 && vaultId == 0) {
    \t\t\tcitizenStatus.hasVault = true;
    \t\t\tvaultId = citizenVaultId;
    \t\t/*
    \t\t\tOtherwise, if the S1 Citizen has no component Vault, the newly-provided 
    \t\t\tVault is staked and the S1 Citizen is recorded as carrying an optional, 
    \t\t\tseparately-attached vault.
    \t\t*/
    \t\t} else if (citizenVaultId == 0 && vaultId != 0) {
    \t\t\t_assetTransferFrom(VAULT, msg.sender, address(this), vaultId);
    \t\t\tcitizenStatus.hasVault = true;
    \t\t\tcitizenStatus.stakedVaultId = vaultId;
    \t\t}
    \t\t/*
    \t\t\tIf the S1 Citizen contains no component Vault and is not staked alongside 
    \t\t\tan optional Vault (`citizenVaultId` == 0 && `vaultId` == 0), we need not 
    \t\t\tdo anything to change the initial state of a staked S1 Citizen's Vault.
    \t\t*/
    \t\t// Determine the base worth in points of the S1 Citizen's Identity.
    \t\tstring memory citizenCreditYield = getCreditYield(
    \t\t\tcitizenId,
    \t\t\tcitizenVaultId
    \t\t);
    \t\tuint256 identityPoints = identityCreditPoints[citizenCreditYield];
    \t\t// Hands of the Citadel are always given the same multiplier as '?' Vaults.
    \t\tuint256 vaultMultiplier = 100;
    \t\tif (handClaimant == 1) {
    \t\t\tuint256 identityId = citizen.getIdentityIdOfTokenId(citizenId);
    \t\t\tstring memory class = IGenericGetter(IDENTITY).getClass(identityId);
    \t\t\tif (_stringEquals(class, "Hand of Citadel")) {
    \t\t\t\tvaultMultiplier = vaultCreditMultiplier["?"];
    \t\t\t} else {
    \t\t\t\trevert CitizenIsNotHand(citizenId);
    \t\t\t}
    \t\t// Otherwise use the configured Vault multiplier, if any.
    \t\t} else if (vaultId != 0) {
    \t\t\tvaultMultiplier = getConfiguredVaultMultiplier(vaultId);
    \t\t}
    \t\t// Decode the timelock option's duration and multiplier.
    \t\tuint256 timelockDuration = _timelock >> 128;
    \t\tuint256 timelockMultiplier = _timelock & type(uint128).max;
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.S1_CITIZEN];
    \t\tunchecked {
    \t\t\tcitizenStatus.points =
    \t\t\t\tidentityPoints * vaultMultiplier * timelockMultiplier /
    \t\t\t\t_DIVISOR / _DIVISOR;
    \t\t\tcitizenStatus.timelockEndTime = block.timestamp + timelockDuration;
    \t\t\t// Record the caller's staked S1 Citizen.
    \t\t\t_stakerS1Position[msg.sender].push(citizenId);
    \t\t\t// Update stakers missed rewards.
    \t\t\tpool.rewardsMissed[msg.sender] += 
    \t\t\t\tpool.rewardPerPoint * citizenStatus.points;
    \t\t\t// Update the pool point weights for rewards
    \t\t\tpool.totalPoints += citizenStatus.points;
    \t\t}
    \t\t// mint the soulbound token upon staking
            INTStakedToken(NT_STAKED_CITIZEN).give(msg.sender, abi.encode(S1_CITIZEN, uint64(citizenId)), "");
    \t\t// Emit an event recording this S1 Citizen staking.
    \t\temit Stake(
    \t\t\tmsg.sender,
    \t\t\tS1_CITIZEN,
    \t\t\t_timelock,
    \t\t\tcitizenId
    \t\t);
    \t}
    \t/**
    \t\tA private function for managing the staking of a particular S2 Citizen.
    \t\t@param _timelock The selected timelock option for the asset being staked. 
    \t\t\tThis encodes the timelock duration and multiplier.
    \t*/
    \tfunction _stakeS2Citizen (
    \t\tuint256 _timelock
    \t) private {
    \t\tuint256 citizenId;
    \t\t// Extract the S2 Citizen ID from the calldata.
    \t\tassembly {
    \t\t\tcitizenId := calldataload(0x44)
    \t\t}
    \t\t/*
    \t\t\tAttempt to transfer the S2 Citizen to be held in escrow by this staking 
    \t\t\tcontract. This transfer will fail if the caller is not the holder of the 
    \t\t\tCitizen. This prevents double staking.
    \t\t*/
    \t\t_assetTransferFrom(S2_CITIZEN, msg.sender, address(this), citizenId);
    \t\t// Retrieve storage for tracking the staking state of this S2 Citizen.
    \t\tStakedS2Citizen storage citizenStatus = stakedS2[msg.sender][citizenId];
    \t\t// Decode the timelock option's duration and multiplier.
    \t\tuint256 timelockDuration = _timelock >> 128;
    \t\tuint256 timelockMultiplier = _timelock & type(uint128).max;
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.S2_CITIZEN];
    \t\tunchecked {
    \t\t\tcitizenStatus.points = 100 * timelockMultiplier / _DIVISOR;
    \t\t\tcitizenStatus.timelockEndTime = block.timestamp + timelockDuration;
    \t\t\t// Record the caller's staked S2 Citizen.
    \t\t\t_stakerS2Position[msg.sender].push(citizenId);
    \t\t\t// Update the pool point weights for rewards
    \t\t\tpool.totalPoints += citizenStatus.points;
    \t\t\t// Update stakers missed rewards.
    \t\t\tpool.rewardsMissed[msg.sender] += 
    \t\t\t\tpool.rewardPerPoint * citizenStatus.points;
    \t\t}
    \t\t// mint the soulbound token upon staking
            INTStakedToken(NT_STAKED_CITIZEN).give(msg.sender, abi.encode(S2_CITIZEN, uint64(citizenId)), "");
    \t\t// Emit an event recording this S1 Citizen staking.
    \t\temit Stake(
    \t\t\tmsg.sender,
    \t\t\tS2_CITIZEN,
    \t\t\t_timelock,
    \t\t\tcitizenId
    \t\t);
    \t}
    \t/**
    \t\tA private function for managing the staking of BYTES into a Citizen.
    \t*/
    \tfunction _stakeBytes (
    \t\tuint256
    \t) private {
    \t\tuint256 amount;
    \t\tuint256 citizenId;
    \t\tuint256 seasonId;
    \t\tassembly{
    \t\t\tamount := calldataload(0x44)
    \t\t\tcitizenId := calldataload(0x64)
    \t\t\tseasonId := calldataload(0x84)
    \t\t}
    \t\t// Attempt to transfer BYTES to escrow.
    \t\t_assetTransferFrom(BYTES, msg.sender, address(this), amount);
    \t\t// Handle staking BYTES into an S1 Citizen.
    \t\tif (seasonId == 1) {
    \t\t\tStakedS1Citizen storage citizenStatus = stakedS1[msg.sender][citizenId];
    \t\t\tuint256 cap = VAULT_CAP;
    \t\t\tif (!citizenStatus.hasVault) {
    \t\t\t\tcap = NO_VAULT_CAP;
    \t\t\t}
    \t\t\tif (citizenStatus.stakedBytes + amount > cap) {
    \t\t\t\trevert AmountExceedsCap(citizenStatus.stakedBytes + amount, cap);
    \t\t\t}
    \t\t\t// Validate that the caller actually staked the Citizen.
    \t\t\tif (citizenStatus.timelockEndTime == 0) {
    \t\t\t\trevert CannotStakeIntoUnownedCitizen(citizenId, seasonId);
    \t\t\t}
    \t\t\tPoolData storage pool = _pools[AssetType.S1_CITIZEN];
    \t\t\tunchecked {
    \t\t\t\tuint256 bonusPoints = (amount * 100 / _BYTES_PER_POINT);
    \t\t\t\tcitizenStatus.stakedBytes += amount;
    \t\t\t\tcitizenStatus.points += bonusPoints;
    \t\t\t\tpool.totalPoints += bonusPoints;
    \t\t\t\t// Update stakers missed rewards.
    \t\t\t\tpool.rewardsMissed[msg.sender] += 
    \t\t\t\t\tpool.rewardPerPoint * bonusPoints;
    \t\t\t}
    \t\t// Handle staking BYTES into an S2 Citizen.
    \t\t} else if (seasonId == 2) {
    \t\t\tStakedS2Citizen storage citizenStatus = stakedS2[msg.sender][citizenId];
    \t\t\tuint256 cap = NO_VAULT_CAP;
    \t\t\tif (citizenStatus.stakedBytes + amount > cap) {
    \t\t\t\trevert AmountExceedsCap(citizenStatus.stakedBytes + amount, cap);
    \t\t\t}
    \t\t\t// Validate that the caller actually staked the Citizen.
    \t\t\tif (citizenStatus.timelockEndTime == 0) {
    \t\t\t\trevert CannotStakeIntoUnownedCitizen(citizenId, seasonId);
    \t\t\t}
    \t\t\tPoolData storage pool = _pools[AssetType.S2_CITIZEN];
    \t\t\tunchecked {
    \t\t\t\tuint256 bonusPoints = (amount * 100 / _BYTES_PER_POINT);
    \t\t\t\tcitizenStatus.stakedBytes += amount;
    \t\t\t\tcitizenStatus.points += bonusPoints;
    \t\t\t\tpool.totalPoints += bonusPoints;
    \t\t\t\t// Update stakers missed rewards.
    \t\t\t\tpool.rewardsMissed[msg.sender] += 
    \t\t\t\t\tpool.rewardPerPoint * bonusPoints;
    \t\t\t}
    \t\t// Revert because an invalid season ID has been supplied.
    \t\t} else {
    \t\t\trevert InvalidSeasonId(seasonId);
    \t\t}
    \t\t// Emit an event.
    \t\temit Stake(
    \t\t\tmsg.sender,
    \t\t\tBYTES,
    \t\t\t(seasonId << 128) + citizenId,
    \t\t\tamount
    \t\t);
    \t}
    \t/**
    \t\tA private function for managing the staking of LP tokens.
    \t\t@param _timelock The selected timelock option for the asset being staked. 
    \t\t\tThis encodes the timelock duration and multiplier.
    \t*/
    \tfunction _stakeLP (
    \t\tuint256 _timelock
    \t) private {
    \t\tuint256 amount;
    \t\tassembly{
    \t\t\tamount := calldataload(0x44)
    \t\t}
    \t\t/*
    \t\t\tAttempt to transfer the LP tokens to be held in escrow by this staking 
    \t\t\tcontract. This transfer will fail if the caller does not hold enough 
    \t\t\ttokens.
    \t\t*/
    \t\t_assetTransferFrom(LP, msg.sender, address(this), amount);
    \t\t// Decode the timelock option's duration and multiplier.
    \t\tuint256 timelockDuration = _timelock >> 128;
    \t\tuint256 timelockMultiplier = _timelock & type(uint128).max;
    \t\t// If this is a new stake of this asset, initialize the multiplier details.
    \t\tif (stakerLPPosition[msg.sender].multiplier == 0) {
    \t\t\tstakerLPPosition[msg.sender].multiplier = timelockMultiplier;
    \t\t// If a multiplier exists already, we must match it.
    \t\t} else if (stakerLPPosition[msg.sender].multiplier != timelockMultiplier) {
    \t\t\trevert MismatchedTimelock();
    \t\t}
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.LP];
    \t\tunchecked {
    \t\t\tuint256 points = amount * 100 / 1e18 * timelockMultiplier / _DIVISOR;
    \t\t\t// Update the caller's LP token stake.
    \t\t\tstakerLPPosition[msg.sender].timelockEndTime =
    \t\t\t\tblock.timestamp + timelockDuration;
    \t\t\tstakerLPPosition[msg.sender].amount += amount;
    \t\t\tstakerLPPosition[msg.sender].points += points;
    \t\t\t/// Update stakers missed rewards.
    \t\t\tpool.rewardsMissed[msg.sender] += 
    \t\t\t\tpool.rewardPerPoint * points;
    \t\t\t// Update the pool point weights for rewards.
    \t\t\tpool.totalPoints += points;
    \t\t}
    \t\t// Emit an event recording this LP staking.
    \t\temit Stake(
    \t\t\tmsg.sender,
    \t\t\tLP,
    \t\t\t_timelock,
    \t\t\tamount
    \t\t);
    \t}
    \t/**
    \t\tUse the emission schedule of a particular asset pool to calculate the total
    \t\tamount of staking reward token emitted between two specified timestamps.
    \t\t@param _assetType The type of the asset to calculate emissions for.
    \t\t@param _from The time to begin calculating emissions from.
    \t*/
    \tfunction getTotalEmissions (
    \t\tAssetType _assetType,
    \t\tuint256 _from
    \t) public view returns (uint256) {
    \t\tPoolData storage pool = _pools[_assetType];
    \t\t/*
    \t\t\t\tDetermine the reward for the `_recipient` based on their points total. 
    \t\t\t\tIterate through the entire array of pool reward windows to find the 
    \t\t\t\tapplicable time period.
    \t\t\t*/
    \t\t\tuint256 totalReward;
    \t\t\tuint256 windowCount = pool.rewardCount;
    \t\t\tfor (uint256 i; i < windowCount; ) {
    \t\t\t\tRewardWindow memory window = pool.rewardWindows[i];
    \t\t\t\t/*
    \t\t\t\t\tIf the last reward time is less than the starting time of this 
    \t\t\t\t\twindow, then the reward was accrued in the previous window.
    \t\t\t\t*/
    \t\t\t\tif (_from < window.startTime) {
    \t\t\t\t\tuint256 currentRewardRate = pool.rewardWindows[i - 1].reward;
    \t\t\t\t\t/*
    \t\t\t\t\t\tIterate forward to the present timestamp over any unclaimed reward 
    \t\t\t\t\t\twindows.
    \t\t\t\t\t*/
    \t\t\t\t\tfor (uint256 j = i; j < windowCount; ) {
    \t\t\t\t\t\t// If the current time falls within this window, complete.
    \t\t\t\t\t\tif (block.timestamp <= window.startTime) {
    \t\t\t\t\t\t\tunchecked {
    \t\t\t\t\t\t\t\tuint256 timeSinceReward = block.timestamp - _from;
    \t\t\t\t\t\t\t\ttotalReward += currentRewardRate * timeSinceReward;\t
    \t\t\t\t\t\t\t}
    \t\t\t\t\t\t\t// We have no forward goto and thus include this bastardry.
    \t\t\t\t\t\t\ti = windowCount;
    \t\t\t\t\t\t\tbreak;
    \t\t\t\t\t\t// Otherwise, accrue the remainder of this window and iterate.
    \t\t\t\t\t\t} else {
    \t\t\t\t\t\t\tunchecked {
    \t\t\t\t\t\t\t\tuint256 timeSinceReward = window.startTime - _from;
    \t\t\t\t\t\t\t\ttotalReward += currentRewardRate * timeSinceReward;
    \t\t\t\t\t\t\t}
    \t\t\t\t\t\t\tcurrentRewardRate = window.reward;
    \t\t\t\t\t\t\t_from = window.startTime;
    \t\t\t\t\t\t\t/*
    \t\t\t\t\t\t\t\tHandle the special case of overrunning the final window by 
    \t\t\t\t\t\t\t\tfulfilling the prior window and then jumping forward to use the 
    \t\t\t\t\t\t\t\tfinal reward window.
    \t\t\t\t\t\t\t*/
    \t\t\t\t\t\t\tif (j == windowCount - 1) {
    \t\t\t\t\t\t\t\tunchecked {
    \t\t\t\t\t\t\t\t\tuint256 timeSinceReward =
    \t\t\t\t\t\t\t\t\t\tblock.timestamp - _from;
    \t\t\t\t\t\t\t\t\ttotalReward += currentRewardRate * timeSinceReward;
    \t\t\t\t\t\t\t\t}
    \t
    \t\t\t\t\t\t\t\t// We have no forward goto and thus include this bastardry.
    \t\t\t\t\t\t\t\ti = windowCount;
    \t\t\t\t\t\t\t\tbreak;
    \t
    \t\t\t\t\t\t\t// Otherwise, iterate.
    \t\t\t\t\t\t\t} else {
    \t\t\t\t\t\t\t\twindow = pool.rewardWindows[j + 1];
    \t\t\t\t\t\t\t}
    \t\t\t\t\t\t}
    \t\t\t\t\t\tunchecked { j++; }
    \t\t\t\t\t}
    \t\t\t\t/*
    \t\t\t\t\tOtherwise, the last reward rate, and therefore the entireity of 
    \t\t\t\t\taccrual, falls in the last window.
    \t\t\t\t*/
    \t\t\t\t} else if (i == windowCount - 1) {
    \t\t\t\t\tunchecked {
    \t\t\t\t\t\tuint256 timeSinceReward = block.timestamp - _from;
    \t\t\t\t\t\ttotalReward = window.reward * timeSinceReward;
    \t\t\t\t\t}
    \t\t\t\t\tbreak;
    \t\t\t\t}
    \t\t\t\tunchecked { i++; }
    \t\t\t}
    \t\t
    \t\treturn totalReward;
    \t}
      /**
    \t\tThis function supports retrieving the reward and tax earned by a particular 
    \t\t`_recipient` on a specific pool of type `_assetType`. It is meant to be
        called by a frontend interface that needs to show pending rewards.
    \t\t@param _assetType The type of the asset to calculate rewards for.
    \t\t@param _recipient The recipient of the reward.
    \t*/
    \tfunction getPendingPoolReward (
    \t\tAssetType _assetType,
    \t\taddress _recipient
    \t) external view returns (uint256, uint256) {
    \t\t/*
    \t\t\tDuring the very first stake, there will not be any points in the pool. In 
    \t\t\tthis case, do not attempt to grant any rewards so as to prevent reversion.
    \t\t*/
    \t\tPoolData storage pool = _pools[_assetType];
        if (pool.totalPoints == 0) {
    \t\t\treturn (0, 0);
    \t\t}
    \t\t// Calculate rewards for this pool.
    \t\tuint256 totalEmissions = getTotalEmissions(
    \t\t\t_assetType,
    \t\t\tpool.lastUpdated
    \t\t);
    \t\t// Update the pool rewards per point to pay users the amount remaining.
    \t\tuint256 pendingRPP = pool.rewardPerPoint
    \t\t\t+ (totalEmissions / pool.totalPoints);
    \t\t// Calculate the total number of points accrued to the `_recipient`.
    \t\tuint256 points;
    \t\tif (_assetType == AssetType.S1_CITIZEN) {
    \t\t\tfor (uint256 i; i < _stakerS1Position[_recipient].length; ) {
    \t\t\t\tuint256 citizenId = _stakerS1Position[_recipient][i];
    \t\t\t\tStakedS1Citizen memory s1Citizen = stakedS1[_recipient][citizenId];
    \t\t\t\tunchecked {
    \t\t\t\t\tpoints += s1Citizen.points;
    \t\t\t\t\ti++;
    \t\t\t\t}
    \t\t\t}
    \t\t} else if (_assetType == AssetType.S2_CITIZEN) {
    \t\t\tfor (uint256 i; i < _stakerS2Position[_recipient].length; ) {
    \t\t\t\tuint256 citizenId = _stakerS2Position[_recipient][i];
    \t\t\t\tStakedS2Citizen memory s2Citizen = stakedS2[_recipient][citizenId];
    \t\t\t\tunchecked {
    \t\t\t\t\tpoints += s2Citizen.points;
    \t\t\t\t\ti++;
    \t\t\t\t}
    \t\t\t}
    \t\t} else if (_assetType == AssetType.LP) {
    \t\t\tunchecked {
    \t\t\t\tpoints += stakerLPPosition[_recipient].points;
    \t\t\t}
    \t\t} else {
    \t\t\trevert InvalidAssetType(uint256(_assetType));
    \t\t}
    \t\tif (points > 0) {
      \t\tuint256 share = points * pendingRPP
    \t  \t\t- rewardAccrued[_recipient][_assetType]
    \t\t\t\t- pool.rewardsMissed[_recipient];
    \t\t\tuint256 daoShare = share * pool.daoTax / (100 * _DIVISOR);
    \t\t\treturn ((share - daoShare), daoShare);
    \t\t}
    \t\treturn (0, 0);
    \t}
    \t/**
    \t\tUpdate the pool corresponding to the specified asset.
    \t\t
    \t\t@param _assetType The type of the asset to update pool rewards for.
    \t*/
    \tfunction _updatePool (
    \t\tAssetType _assetType
    \t) private {
    \t\tPoolData storage pool = _pools[_assetType];
    \t\tif (pool.totalPoints == 0) {
    \t\t\tpool.lastUpdated = block.timestamp;
    \t\t\treturn;
    \t\t}
    \t\t// Calculate rewards for this pool.
    \t\tuint256 totalEmissions = getTotalEmissions(
    \t\t\t_assetType,
    \t\t\tpool.lastUpdated
    \t\t);
    \t\t// Update the pool rewards per point to pay users the amount remaining.
    \t\tpool.rewardPerPoint = pool.rewardPerPoint
    \t\t\t+ (totalEmissions / pool.totalPoints);
    \t\tpool.lastUpdated = block.timestamp;
    \t}
    \t/**
    \t\tStake a particular asset into this contract, updating its corresponding 
    \t\trewards.
    \t\t@param _assetType An ID of the specific asset that the caller is attempting 
    \t\t\tto deposit into this staker.
    \t\t@param _timelockId The ID of a specific timelock period to select. This 
    \t\t\ttimelock ID must be configured for the specific `_assetType`.
    \t\t@custom:param The third parameter is overloaded to have different meaning 
    \t\t\tdepending on the `assetType` selected. In the event of staking an S1 or 
    \t\t\tS2 Citizen, this parameter is the token ID of the Citizen being staked. 
    \t\t\tIn the event of staking BYTES or LP tokens, this parameter is the amount 
    \t\t\tof the respective token being staked.
    \t\t@custom:param If the asset being staked is an S1 Citizen, this is the ID of 
    \t\t\ta Vault to attempt to optionally attach.
    \t\t@custom:param If the asset being staked is an S1 Citizen, this is a flag to 
    \t\t\tattempt to claim a Hand of the Citadel bonus. If the asset being staked 
    \t\t\tis BYTES, this is either one or two to select the Neo Tokyo season ID of 
    \t\t\tthe S1 or S2 Citizen that BYTES are being staked into.
    \t*/
    \tfunction stake (
    \t\tAssetType _assetType,
    \t\tuint256 _timelockId,
    \t\tuint256,
    \t\tuint256,
    \t\tuint256
    \t) external nonReentrant {
    \t\t// Validate that the asset being staked is of a valid type.
    \t\tif (uint8(_assetType) > 4) {
    \t\t\trevert InvalidAssetType(uint256(_assetType));
    \t\t}
    \t\t// Validate that the asset being staked matches a configured pool.
    \t\tif (_pools[_assetType].rewardCount == 0) {
    \t\t\trevert UnconfiguredPool(uint256(_assetType));
    \t\t}
    \t\t// Validate that the asset being staked matches an active pool.
    \t\tif (_pools[_assetType].rewardWindows[0].startTime >= block.timestamp) {
    \t\t\trevert InactivePool(uint256(_assetType));
    \t\t}
    \t\t// Validate that the selected timelock option is valid for the staked asset.
    \t\tuint256 timelockOption = timelockOptions[_assetType][_timelockId];
    \t\tif (timelockOption == 0) {
    \t\t\trevert InvalidTimelockOption(uint256(_assetType), _timelockId);
    \t\t}
    \t\t// Update pool rewards.
    \t\t_updatePool(_assetType);
    \t\t// Grant the caller their total rewards with each staking action.
    \t\tIByteContract(BYTES).getReward(msg.sender);
    \t\t// Store references to each available staking function.
    \t\tfunction (uint256) _s1 = _stakeS1Citizen;
    \t\tfunction (uint256) _s2 = _stakeS2Citizen;
    \t\tfunction (uint256) _b = _stakeBytes;
    \t\tfunction (uint256) _lp = _stakeLP;
    \t\t// Select the proper staking function based on the asset type being staked.
    \t\tfunction (uint256) _stake;
    \t\tassembly {
    \t\t\tswitch _assetType
    \t\t\t\tcase 0 {
    \t\t\t\t\t_stake := _s1
    \t\t\t\t}
    \t\t\t\tcase 1 {
    \t\t\t\t\t_stake := _s2
    \t\t\t\t}
    \t\t\t\tcase 2 {
    \t\t\t\t\t_stake := _b
    \t\t\t\t}
    \t\t\t\tcase 3 {
    \t\t\t\t\t_stake := _lp
    \t\t\t\t}
    \t\t\t\tdefault {}
    \t\t}
    \t\t// Invoke the correct staking function.
    \t\t_stake(timelockOption);
    \t}
    \t/**
    \t\tThis function supports retrieving the reward and tax earned by a particular 
    \t\t`_recipient` on a specific pool of type `_assetType`. It is meant to be
        called in conjuction with `_updatePool` and a reward claim.
    \t\t@param _assetType The type of the asset to calculate rewards for.
    \t\t@param _recipient The recipient of the reward.
    \t*/
    \tfunction _getPoolReward (
    \t\tAssetType _assetType,
    \t\taddress _recipient
    \t) private view returns (uint256, uint256) {
    \t\t/*
    \t\t\tDuring the very first stake, there will not be any points in the pool. In 
    \t\t\tthis case, do not attempt to grant any rewards so as to prevent reversion.
    \t\t*/
    \t\tPoolData storage pool = _pools[_assetType];
    \t\t// Calculate the total number of points accrued to the `_recipient`.
    \t\tuint256 points;
    \t\tif (_assetType == AssetType.S1_CITIZEN) {
    \t\t\tfor (uint256 i; i < _stakerS1Position[_recipient].length; ) {
    \t\t\t\tuint256 citizenId = _stakerS1Position[_recipient][i];
    \t\t\t\tStakedS1Citizen memory s1Citizen = stakedS1[_recipient][citizenId];
    \t\t\t\tunchecked {
    \t\t\t\t\tpoints += s1Citizen.points;
    \t\t\t\t\ti++;
    \t\t\t\t}
    \t\t\t}
    \t\t} else if (_assetType == AssetType.S2_CITIZEN) {
    \t\t\tfor (uint256 i; i < _stakerS2Position[_recipient].length; ) {
    \t\t\t\tuint256 citizenId = _stakerS2Position[_recipient][i];
    \t\t\t\tStakedS2Citizen memory s2Citizen = stakedS2[_recipient][citizenId];
    \t\t\t\tunchecked {
    \t\t\t\t\tpoints += s2Citizen.points;
    \t\t\t\t\ti++;
    \t\t\t\t}
    \t\t\t}
    \t\t} else if (_assetType == AssetType.LP) {
    \t\t\tunchecked {
    \t\t\t\tpoints += stakerLPPosition[_recipient].points;
    \t\t\t}
    \t\t} else {
    \t\t\trevert InvalidAssetType(uint256(_assetType));
    \t\t}
    \t\tif (points > 0) {
      \t\tuint256 share = points * pool.rewardPerPoint
    \t  \t\t- rewardAccrued[_recipient][_assetType]
    \t\t\t\t- pool.rewardsMissed[_recipient];
    \t\t\tuint256 daoShare = share * pool.daoTax / (100 * _DIVISOR);
    \t\t\treturn ((share - daoShare), daoShare);
    \t\t}
    \t\treturn (0, 0);
    \t}
    \t/**
    \t\tDetermine the reward, based on staking participation at this moment, of a 
    \t\tparticular recipient. Due to a historic web of Neo Tokyo dependencies, 
    \t\trewards are actually minted through the BYTES contract.
    \t\t@param _recipient The recipient to calculate the reward for.
    \t\t@return A tuple containing (the number of tokens due to be minted to 
    \t\t\t`_recipient` as a reward, and the number of tokens that should be minted 
    \t\t\tto the DAO treasury as a DAO tax).
    \t*/
    \tfunction claimReward (
    \t\taddress _recipient
    \t) external returns (uint256, uint256) {
    \t\t// This function may only be called by the BYTES contract.
    \t\tif (msg.sender != BYTES) {
    \t\t\trevert CallerIsNotBYTES();
    \t\t}
    \t\t// Update pool rewards.
    \t\t_updatePool(AssetType.S1_CITIZEN);
    \t\t_updatePool(AssetType.S2_CITIZEN);
    \t\t_updatePool(AssetType.LP);
    \t\t// Retrieve the `_recipient` reward share from each pool.
    \t\t(uint256 s1Reward, uint256 s1Tax) = _getPoolReward(
    \t\t\tAssetType.S1_CITIZEN,
    \t\t\t_recipient
    \t\t);
    \t\t(uint256 s2Reward, uint256 s2Tax) = _getPoolReward(
    \t\t\tAssetType.S2_CITIZEN,
    \t\t\t_recipient
    \t\t);
    \t\t(uint256 lpReward, uint256 lpTax) = _getPoolReward(
    \t\t\tAssetType.LP,
    \t\t\t_recipient
    \t\t);
    \t\t// Record the current time as the beginning time for checking rewards.
    \t\tlastRewardTime[_recipient][AssetType.S1_CITIZEN] = block.timestamp;
    \t\tlastRewardTime[_recipient][AssetType.S2_CITIZEN] = block.timestamp;
    \t\tlastRewardTime[_recipient][AssetType.LP] = block.timestamp;
    \t\t// Calculate total reward and tax.
    \t\tuint256 totalReward;
    \t\tuint256 totalTax;
    \t\tunchecked {
    \t\t\ttotalReward = (s1Reward + s2Reward + lpReward);
    \t\t\ttotalTax = (s1Tax + s2Tax + lpTax);
    \t\t\trewardAccrued[_recipient][AssetType.S1_CITIZEN] += (s1Reward + s1Tax);
    \t\t\trewardAccrued[_recipient][AssetType.S2_CITIZEN] += (s2Reward + s2Tax);
    \t\t\trewardAccrued[_recipient][AssetType.LP] += (lpReward + lpTax);
    \t\t}
    \t\t// Emit an event.
    \t\temit Claim (
    \t\t\t_recipient,
    \t\t\ttotalReward,
    \t\t\ttotalTax
    \t\t);
    \t\t// Return the final reward for the user and the tax rewarded to the DAO.
    \t\treturn (totalReward, totalTax);
    \t}
    \t/**
    \t\tA private function for managing the withdrawal of S1 Citizens.
    \t*/
    \tfunction _withdrawS1Citizen () private {
    \t\tuint256 citizenId;
    \t\tassembly {
    \t\t\tcitizenId := calldataload(0x24)
    \t\t}
    \t\t// Validate that the caller has cleared their asset timelock.
    \t\tStakedS1Citizen storage stakedCitizen = stakedS1[msg.sender][citizenId];
    \t\tif (block.timestamp < stakedCitizen.timelockEndTime) {
    \t\t\trevert TimelockNotCleared(stakedCitizen.timelockEndTime);
    \t\t}
    \t\t// Validate that the caller actually staked this asset.
    \t\tif (stakedCitizen.timelockEndTime == 0) {
    \t\t\trevert CannotWithdrawUnownedS1(citizenId);
    \t\t}
    \t\t
    \t\t// Return any staked BYTES.
    \t\tif (stakedCitizen.stakedBytes > 0) {
    \t\t\t_assetTransfer(BYTES, msg.sender, stakedCitizen.stakedBytes);
    \t\t}
    \t\t
    \t\t// Return any non-component Vault if one is present.
    \t\tif (stakedCitizen.stakedVaultId != 0) {
    \t\t\t_assetTransferFrom(
    \t\t\t\tVAULT,
    \t\t\t\taddress(this),
    \t\t\t\tmsg.sender,
    \t\t\t\tstakedCitizen.stakedVaultId
    \t\t\t);
    \t\t}
    \t\t// Return the S1 Citizen.
    \t\t_assetTransferFrom(S1_CITIZEN, address(this), msg.sender, citizenId);
    \t\t// burn the soulbound token
    \t\tuint256 tokenId;
    \t\taddress addr = S1_CITIZEN;
            assembly {
                // this is the equivalent of
                // encoded = uint256(addr);
                // encoded <<= 96;
                // encoded |= citizen id
                tokenId := or(shl(96, addr), citizenId)
            }
    \t\tINTStakedToken(NT_STAKED_CITIZEN).burn(tokenId);
    \t\t/*
    \t\t\tCheck each citizen ID to find its index and remove the token from the
    \t\t\tstaked item array of its old position.
    \t\t*/
    \t\tuint256[] storage oldPosition = _stakerS1Position[msg.sender];
    \t\tfor (uint256 stakedIndex; stakedIndex < oldPosition.length; ) {
    \t\t\t// Remove the element at the matching index.
    \t\t\tif (citizenId == oldPosition[stakedIndex]) {
    \t\t\t\tif (stakedIndex != oldPosition.length - 1) {
    \t\t\t\t\toldPosition[stakedIndex] = oldPosition[oldPosition.length - 1];
    \t\t\t\t}
    \t\t\t\toldPosition.pop();
    \t\t\t\tbreak;
    \t\t\t}
    \t\t\tunchecked { stakedIndex++; }
    \t\t}
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.S1_CITIZEN];
    \t\tuint256 leftoverPoints;
    \t\tfor (uint256 i; i < _stakerS1Position[msg.sender].length; ) {
    \t\t\tuint256 leftoverId = _stakerS1Position[msg.sender][i];
    \t\t\tStakedS1Citizen memory s1Citizen = stakedS1[msg.sender][leftoverId];
    \t\t\tunchecked {
    \t\t\t\tleftoverPoints += s1Citizen.points;
    \t\t\t\ti++;
    \t\t\t}
    \t\t}
    \t\tunchecked {
    \t\t\tpool.totalPoints -= stakedCitizen.points;
    \t\t}
    \t\tpool.rewardsMissed[msg.sender] = leftoverPoints * pool.rewardPerPoint;
    \t\tdelete rewardAccrued[msg.sender][AssetType.S1_CITIZEN];
    \t\tstakedCitizen.stakedBytes = 0;
    \t\tstakedCitizen.timelockEndTime = 0;
    \t\tstakedCitizen.points = 0;
    \t\tstakedCitizen.hasVault = false;
    \t\tstakedCitizen.stakedVaultId = 0;
    \t\t// Emit an event recording this S1 withdraw.
    \t\temit Withdraw(
    \t\t\tmsg.sender,
    \t\t\tS1_CITIZEN,
    \t\t\tcitizenId
    \t\t);
    \t}
    \t/**
    \t\tA private function for managing the withdrawal of S2 Citizens.
    \t*/
    \tfunction _withdrawS2Citizen () private {
    \t\tuint256 citizenId;
    \t\tassembly {
    \t\t\tcitizenId := calldataload(0x24)
    \t\t}
    \t\t// Validate that the caller has cleared their asset timelock.
    \t\tStakedS2Citizen storage stakedCitizen = stakedS2[msg.sender][citizenId];
    \t\tif (block.timestamp < stakedCitizen.timelockEndTime) {
    \t\t\trevert TimelockNotCleared(stakedCitizen.timelockEndTime);
    \t\t}
    \t\t// Validate that the caller actually staked this asset.
    \t\tif (stakedCitizen.timelockEndTime == 0) {
    \t\t\trevert CannotWithdrawUnownedS2(citizenId);
    \t\t}
    \t\t// Return any staked BYTES.
    \t\tif (stakedCitizen.stakedBytes > 0) {
    \t\t\t_assetTransfer(BYTES, msg.sender, stakedCitizen.stakedBytes);
    \t\t}
    \t\t// Return the S2 Citizen.
    \t\t_assetTransferFrom(S2_CITIZEN, address(this), msg.sender, citizenId);
    \t\t// burn the soulbound token
    \t\tuint256 tokenId;
    \t\taddress addr = S2_CITIZEN;
            assembly {
                // this is the equivalent of
                // encoded = uint256(addr);
                // encoded <<= 96;
                // encoded |= citizen id
                tokenId := or(shl(96, addr), citizenId)
            }
    \t\tINTStakedToken(NT_STAKED_CITIZEN).burn(tokenId);
    \t\t/*
    \t\t\tCheck each citizen ID to find its index and remove the token from the
    \t\t\tstaked item array of its old position.
    \t\t*/
    \t\tuint256[] storage oldPosition = _stakerS2Position[msg.sender];
    \t\tfor (uint256 stakedIndex; stakedIndex < oldPosition.length; ) {
    \t\t\t// Remove the element at the matching index.
    \t\t\tif (citizenId == oldPosition[stakedIndex]) {
    \t\t\t\tif (stakedIndex != oldPosition.length - 1) {
    \t\t\t\t\toldPosition[stakedIndex] = oldPosition[oldPosition.length - 1];
    \t\t\t\t}
    \t\t\t\toldPosition.pop();
    \t\t\t\tbreak;
    \t\t\t}
    \t\t\tunchecked { stakedIndex++; }
    \t\t}
    \t\t// Calculate the caller's leftover points.
    \t\tuint256 leftoverPoints;
    \t\tfor (uint256 i; i < _stakerS2Position[msg.sender].length; ) {
    \t\t\tuint256 leftoverId = _stakerS2Position[msg.sender][i];
    \t\t\tStakedS2Citizen memory s2Citizen = stakedS2[msg.sender][leftoverId];
    \t\t\tunchecked {
    \t\t\t\tleftoverPoints += s2Citizen.points;
    \t\t\t\ti++;
    \t\t\t}
    \t\t}
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.S2_CITIZEN];
    \t\tunchecked {
    \t\t\tpool.totalPoints -= stakedCitizen.points;
    \t\t}
    \t\tpool.rewardsMissed[msg.sender] = leftoverPoints * pool.rewardPerPoint;
    \t\tdelete rewardAccrued[msg.sender][AssetType.S2_CITIZEN];
    \t\tstakedCitizen.stakedBytes = 0;
    \t\tstakedCitizen.timelockEndTime = 0;
    \t\tstakedCitizen.points = 0;
    \t\t// Emit an event recording this S2 withdraw.
    \t\temit Withdraw(
    \t\t\tmsg.sender,
    \t\t\tS2_CITIZEN,
    \t\t\tcitizenId
    \t\t);
    \t}
    \t/**
    \t\tA private function for managing the withdrawal of LP tokens.
    \t*/
    \tfunction _withdrawLP () private {
    \t\tuint256 amount;
    \t\tassembly{
    \t\t\tamount := calldataload(0x24)
    \t\t}
    \t\t// Validate that the caller has cleared their asset timelock.
    \t\tLPPosition storage lpPosition = stakerLPPosition[msg.sender];
    \t\tif (block.timestamp < lpPosition.timelockEndTime) {
    \t\t\trevert TimelockNotCleared(lpPosition.timelockEndTime);
    \t\t}
    \t\t// Validate that the caller has enough staked LP tokens to withdraw.
    \t\tif (lpPosition.amount < amount) {
    \t\t\trevert NotEnoughLPTokens(amount, lpPosition.amount);
    \t\t}
    \t\t/*
    \t\t\tAttempt to transfer the LP tokens held in escrow by this staking contract 
    \t\t\tback to the caller.
    \t\t*/
    \t\t_assetTransfer(LP, msg.sender, amount);
    \t\t// Update caller staking information and asset data.
    \t\tPoolData storage pool = _pools[AssetType.LP];
    \t\tunchecked {
    \t\t\tuint256 points = amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR;
    \t\t\tuint256 pointsIntact = points > lpPosition.points ? 0 : lpPosition.points - points;
    \t\t\tdelete rewardAccrued[msg.sender][AssetType.LP];
    \t\t\tpool.rewardsMissed[msg.sender] = pointsIntact * pool.rewardCount;
    \t\t\t// Update the caller's LP token stake.
    \t\t\tlpPosition.amount -= amount;
    \t\t\tlpPosition.points = pointsIntact;
    \t\t\t// Update the pool point weights for rewards.
    \t\t\tpool.totalPoints -= points;
    \t\t}
    \t\t// If all LP tokens are withdrawn, we must clear the multiplier.
    \t\tif (lpPosition.amount == 0) {
    \t\t\tlpPosition.multiplier = 0;
    \t\t}
    \t\t// Emit an event recording this LP withdraw.
    \t\temit Withdraw(
    \t\t\tmsg.sender,
    \t\t\tLP,
    \t\t\tamount
    \t\t);
    \t}
    \t/**
    \t\tWithdraw a particular asset from this contract, updating its corresponding 
    \t\trewards. A caller may only withdraw an asset provided they are the staker 
    \t\tand that timelocks are not violated.
    \t\t@param _assetType An ID of the specific asset that the caller is attempting 
    \t\t\tto withdraw from this staker.
    \t\t@custom:param The third parameter is overloaded to have different meaning 
    \t\t\tdepending on the `assetType` selected. In the event of withdrawing an S1 
    \t\t\tor S2 Citizen, this is the token ID of the Citizen to attempt to 
    \t\t\twithdraw. In the event of withdrawing LP tokens, this is the amount of 
    \t\t\tthe LP token to withdraw.
    \t*/
    \tfunction withdraw (
    \t\tAssetType _assetType,
    \t\tuint256
    \t) external nonReentrant {
    \t\t/*
    \t\t\tValidate that the asset being withdrawn is of a valid type. BYTES may not 
    \t\t\tbe withdrawn independently of the Citizen that they are staked into.
    \t\t*/
    \t\tif (uint8(_assetType) == 2 || uint8(_assetType) > 4) {
    \t\t\trevert InvalidAssetType(uint256(_assetType));
    \t\t}
    \t\t// Update pool rewards.
    \t\t_updatePool(_assetType);
    \t\t// Grant the caller their total rewards with each withdrawal action.
    \t\tIByteContract(BYTES).getReward(msg.sender);
    \t\t// Store references to each available withdraw function.
    \t\tfunction () _s1 = _withdrawS1Citizen;
    \t\tfunction () _s2 = _withdrawS2Citizen;
    \t\tfunction () _lp = _withdrawLP;
    \t\t// Select the proper withdraw function based on the asset type.
    \t\tfunction () _withdraw;
    \t\tassembly {
    \t\t\tswitch _assetType
    \t\t\t\tcase 0 {
    \t\t\t\t\t_withdraw := _s1
    \t\t\t\t}
    \t\t\t\tcase 1 {
    \t\t\t\t\t_withdraw := _s2
    \t\t\t\t}
    \t\t\t\tcase 3 {
    \t\t\t\t\t_withdraw := _lp
    \t\t\t\t}
    \t\t\t\tdefault {}
    \t\t}
    \t\t// Invoke the correct withdraw function.
    \t\t_withdraw();
    \t}
    \t/**
    \t\tThis function allows a permitted user to configure the LP token contract 
    \t\taddress. Extreme care must be taken to avoid doing this if there are any LP 
    \t\tstakers, lest staker funds be lost. It is recommended that `lockLP` be 
    \t\tinvoked.
    \t\t@param _lp The address of the LP token contract to specify.
    \t*/
    \tfunction configureLP (
    \t\taddress _lp
    \t) external hasValidPermit(UNIVERSAL, CONFIGURE_LP) {
    \t\tif (lpLocked) {
    \t\t\trevert LockedConfigurationOfLP();
    \t\t}
    \t\tLP = _lp;
    \t}
    \t/**
    \t\tThis function allows a permitted user to forever prevent alteration of the 
    \t\tLP token contract address.
    \t*/
    \tfunction lockLP () external hasValidPermit(UNIVERSAL, CONFIGURE_LP) {
    \t\tlpLocked = true;
    \t}
    \t/**
    \t\tThis function allows a permitted user to configure the timelock options 
    \t\tavailable for each type of asset.
    \t\t@param _assetType The type of asset whose timelock options are being 
    \t\t\tconfigured.
    \t\t@param _timelockIds An array with IDs for specific timelock options 
    \t\t\tavailable under `_assetType`.
    \t\t@param _encodedSettings An array keyed to `_timelockIds` containing a 
    \t\t\tbit-packed value specifying the details of the timelock. The upper 128 
    \t\t\tbits are the timelock duration and the lower 128 bits are the multiplier.
    \t*/
    \tfunction configureTimelockOptions (
    \t\tAssetType _assetType,
    \t\tuint256[] memory _timelockIds,
    \t\tuint256[] memory _encodedSettings
    \t) external hasValidPermit(bytes32(uint256(_assetType)), CONFIGURE_TIMELOCKS) {
    \t\tfor (uint256 i; i < _timelockIds.length; ) {
    \t\t\ttimelockOptions[_assetType][_timelockIds[i]] = _encodedSettings[i];
    \t\t\tunchecked { ++i; }
    \t\t}
    \t}
    \t/**
    \t\tThis function allows a permitted user to configure the double mapping of 
    \t\tcombined S1 Citizen reward rates and Vault reward credit multipliers 
    \t\trequired to deduce the resulting S1 Identity "Credit Yield" strings.
    \t\t@param _citizenRewardRates An array of S1 Citizen reward rate values.
    \t\t@param _vaultRewardRates An array of Vault reward rate multipliers 
    \t\t\tcorresponding to the provided `_citizenRewardRates`.
    \t\t@param _identityCreditYields An array of the S1 Identity "Credit Yield" 
    \t\t\tstrings that must correspond to the provided `_citizenRewardRates` and 
    \t\t\t`_vaultRewardRates`.
    \t*/
    \tfunction configureIdentityCreditYields (
    \t\tuint256[] memory _citizenRewardRates, 
    \t\tstring[] memory _vaultRewardRates,
    \t\tstring[] memory _identityCreditYields
    \t) hasValidPermit(UNIVERSAL, CONFIGURE_CREDITS) external {
    \t\tfor (uint256 i; i < _citizenRewardRates.length; ) {
    \t\t\tidentityCreditYield[
    \t\t\t\t_citizenRewardRates[i]
    \t\t\t][
    \t\t\t\t_vaultRewardRates[i]
    \t\t\t] = _identityCreditYields[i];
    \t\t\tunchecked { ++i; }
    \t\t}
    \t}
    \t/**
    \t\tThis funciton allows a permitted user to override the base points 
    \t\tassociated with a particular S1 Identity "Credit Yield" string.
    \t\t@param _identityCreditYields An array of S1 Identity "Credit Yield" strings.
    \t\t@param _points The base points associated with each value in 
    \t\t\t`_identityCreditYields`.
    \t*/
    \tfunction configureIdentityCreditPoints (
    \t\tstring[] memory _identityCreditYields,
    \t\tuint256[] memory _points
    \t) hasValidPermit(UNIVERSAL, CONFIGURE_CREDITS) external {
    \t\tfor (uint256 i; i < _identityCreditYields.length; ) {
    \t\t\tidentityCreditPoints[_identityCreditYields[i]] = _points[i];
    \t\t\tunchecked { ++i; }
    \t\t}
    \t}
    \t/**
    \t\tThis function allows a permitted user to override the S1 Vault multiplier 
    \t\trates associated with a particular S1 Vault "credit multiplier" string.
    \t\t@param _vaultCreditMultipliers An array of S1 Vault credit multiplier 
    \t\t\tstrings.
    \t\t@param _multipliers An array of multipliers, in basis points, keyed to each 
    \t\t\tvalue in `_vaultCreditMultipliers`.
    \t*/
    \tfunction configureVaultCreditMultipliers (
    \t\tstring[] memory _vaultCreditMultipliers,
    \t\tuint256[] memory _multipliers
    \t) hasValidPermit(UNIVERSAL, CONFIGURE_CREDITS) external {
    \t\tfor (uint256 i; i < _vaultCreditMultipliers.length; ) {
    \t\t\tvaultCreditMultiplier[_vaultCreditMultipliers[i]] = _multipliers[i];
    \t\t\tunchecked { ++i; }
    \t\t}
    \t}
    \t/**
    \t\tThis function allows a permitted user to set the reward emission and DAO 
    \t\ttax rates of the asset staking pools.
    \t\t@param _inputs An array of `PoolConfigurationInput` structs defining 
    \t\t\tconfiguration details for each of the pools being updated.
    \t*/
    \tfunction configurePools (
    \t\tPoolConfigurationInput[] memory _inputs
    \t) hasValidPermit(UNIVERSAL, CONFIGURE_POOLS) external {
    \t\tfor (uint256 i; i < _inputs.length; ) {
    \t\t\tuint256 poolRewardWindowCount = _inputs[i].rewardWindows.length;
    \t\t\t_pools[_inputs[i].assetType].rewardCount = poolRewardWindowCount;
    \t\t\t_pools[_inputs[i].assetType].daoTax = _inputs[i].daoTax;
    \t\t\t// Set the pool reward window details by populating the mapping.
    \t\t\tuint256 lastTime;
    \t\t\tfor (uint256 j; j < poolRewardWindowCount; ) {
    \t\t\t\t_pools[_inputs[i].assetType].rewardWindows[j] =
    \t\t\t\t\t_inputs[i].rewardWindows[j];
    \t\t\t\t// Revert if an invalid pool configuration is supplied.
    \t\t\t\tif (j != 0 && _inputs[i].rewardWindows[j].startTime <= lastTime) {
    \t\t\t\t\trevert RewardWindowTimesMustIncrease();
    \t\t\t\t}
    \t\t\t\tlastTime = _inputs[i].rewardWindows[j].startTime;
    \t\t\t\tunchecked { j++; }
    \t\t\t}
    \t\t\tunchecked { ++i; }
    \t\t}
    \t}
    \t/**
    \t\tThis function allows a permitted user to update the vaulted and unvaulted 
    \t\tCitizen BYTES staking caps.
    \t\t@param _vaultedCap The new cap of BYTES staking on vaulted Citizens.
    \t\t@param _unvaultedCap The new cap of BYTES staking on unvaulted Citizens.
    \t*/
    \tfunction configureCaps (
    \t\tuint256 _vaultedCap,
    \t\tuint256 _unvaultedCap
    \t) hasValidPermit(UNIVERSAL, CONFIGURE_CAPS) external {
    \t\tVAULT_CAP = _vaultedCap;
    \t\tNO_VAULT_CAP = _unvaultedCap;
    \t}
    }
    // SPDX-License-Identifier: MIT
    // 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 ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor() {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            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;
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title An advanced permission-management contract.
    \t@author Tim Clancy <@_Enoch>
    \tThis contract allows for a contract owner to delegate specific rights to
    \texternal addresses. Additionally, these rights can be gated behind certain
    \tsets of circumstances and granted expiration times. This is useful for some
    \tmore finely-grained access control in contracts.
    \tThe owner of this contract is always a fully-permissioned super-administrator.
    \t@custom:date August 23rd, 2021.
    */
    abstract contract PermitControl is Ownable {
    \tusing Address for address;
    \t/// A special reserved constant for representing no rights.
    \tbytes32 public constant ZERO_RIGHT = hex"00000000000000000000000000000000";
    \t/// A special constant specifying the unique, universal-rights circumstance.
    \tbytes32 public constant UNIVERSAL = hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
    \t/**
    \t\tA special constant specifying the unique manager right. This right allows an
    \t\taddress to freely-manipulate the `managedRight` mapping.
    \t*/
    \tbytes32 public constant MANAGER = hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
    \t/**
    \t\tA mapping of per-address permissions to the circumstances, represented as
    \t\tan additional layer of generic bytes32 data, under which the addresses have
    \t\tvarious permits. A permit in this sense is represented by a per-circumstance
    \t\tmapping which couples some right, represented as a generic bytes32, to an
    \t\texpiration time wherein the right may no longer be exercised. An expiration
    \t\ttime of 0 indicates that there is in fact no permit for the specified
    \t\taddress to exercise the specified right under the specified circumstance.
    \t\t@dev Universal rights MUST be stored under the 0xFFFFFFFFFFFFFFFFFFFFFFFF...
    \t\tmax-integer circumstance. Perpetual rights may be given an expiry time of
    \t\tmax-integer.
    \t*/
    \tmapping ( address => mapping( bytes32 => mapping( bytes32 => uint256 ))) 
    \t\tpublic permissions;
    \t/**
    \t\tAn additional mapping of managed rights to manager rights. This mapping
    \t\trepresents the administrator relationship that various rights have with one
    \t\tanother. An address with a manager right may freely set permits for that
    \t\tmanager right's managed rights. Each right may be managed by only one other
    \t\tright.
    \t*/
    \tmapping ( bytes32 => bytes32 ) public managerRight;
    \t/**
    \t\tAn event emitted when an address has a permit updated. This event captures,
    \t\tthrough its various parameter combinations, the cases of granting a permit,
    \t\tupdating the expiration time of a permit, or revoking a permit.
    \t\t@param updater The address which has updated the permit.
    \t\t@param updatee The address whose permit was updated.
    \t\t@param circumstance The circumstance wherein the permit was updated.
    \t\t@param role The role which was updated.
    \t\t@param expirationTime The time when the permit expires.
    \t*/
    \tevent PermitUpdated (
    \t\taddress indexed updater,
    \t\taddress indexed updatee,
    \t\tbytes32 circumstance,
    \t\tbytes32 indexed role,
    \t\tuint256 expirationTime
    \t);
    \t/**
    \t\tAn event emitted when a management relationship in `managerRight` is
    \t\tupdated. This event captures adding and revoking management permissions via
    \t\tobserving the update history of the `managerRight` value.
    \t\t@param manager The address of the manager performing this update.
    \t\t@param managedRight The right which had its manager updated.
    \t\t@param managerRight The new manager right which was updated to.
    \t*/
    \tevent ManagementUpdated (
    \t\taddress indexed manager,
    \t\tbytes32 indexed managedRight,
    \t\tbytes32 indexed managerRight
    \t);
    \t/**
    \t\tA modifier which allows only the super-administrative owner or addresses
    \t\twith a specified valid right to perform a call.
    \t\t@param _circumstance The circumstance under which to check for the validity
    \t\t\tof the specified `right`.
    \t\t@param _right The right to validate for the calling address. It must be
    \t\t\tnon-expired and exist within the specified `_circumstance`.
    \t*/
    \tmodifier hasValidPermit (
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) {
    \t\trequire(
    \t\t\t_msgSender() == owner() || hasRight(_msgSender(), _circumstance, _right),
    \t\t\t"P1"
    \t\t);
    \t\t_;
    \t}
    \t/**
    \t\tSet the `_managerRight` whose `UNIVERSAL` holders may freely manage the
    \t\tspecified `_managedRight`.
    \t\t@param _managedRight The right which is to have its manager set to
    \t\t\t`_managerRight`.
    \t\t@param _managerRight The right whose `UNIVERSAL` holders may manage
    \t\t\t`_managedRight`.
    \t*/
    \tfunction setManagerRight (
    \t\tbytes32 _managedRight,
    \t\tbytes32 _managerRight
    \t) external virtual hasValidPermit(UNIVERSAL, MANAGER) {
    \t\trequire(_managedRight != ZERO_RIGHT, "P3");
    \t\tmanagerRight[_managedRight] = _managerRight;
    \t\temit ManagementUpdated(_msgSender(), _managedRight, _managerRight);
    \t}
    \t/**
    \t\tSet the permit to a specific address under some circumstances. A permit may
    \t\tonly be set by the super-administrative contract owner or an address holding
    \t\tsome delegated management permit.
    \t\t@param _address The address to assign the specified `_right` to.
    \t\t@param _circumstance The circumstance in which the `_right` is valid.
    \t\t@param _right The specific right to assign.
    \t\t@param _expirationTime The time when the `_right` expires for the provided
    \t\t\t`_circumstance`.
    \t*/
    \tfunction setPermit (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right,
    \t\tuint256 _expirationTime
    \t) public virtual hasValidPermit(UNIVERSAL, managerRight[_right]) {
    \t\trequire(_right != ZERO_RIGHT, "P2");
    \t\tpermissions[_address][_circumstance][_right] = _expirationTime;
    \t\temit PermitUpdated(
    \t\t\t_msgSender(),
    \t\t\t_address,
    \t\t\t_circumstance,
    \t\t\t_right,
    \t\t\t_expirationTime
    \t\t);
    \t}
    \t/**
    \t\tDetermine whether or not an address has some rights under the given
    \t\tcircumstance, and if they do have the right, until when.
    \t\t@param _address The address to check for the specified `_right`.
    \t\t@param _circumstance The circumstance to check the specified `_right` for.
    \t\t@param _right The right to check for validity.
    \t\t@return The timestamp in seconds when the `_right` expires. If the timestamp
    \t\t\tis zero, we can assume that the user never had the right.
    \t*/
    \tfunction hasRightUntil (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) public view returns (uint256) {
    \t\treturn permissions[_address][_circumstance][_right];
    \t}
    \t/**
    \t\tDetermine whether or not an address has some rights under the given
    \t\tcircumstance,
    \t\t@param _address The address to check for the specified `_right`.
    \t\t@param _circumstance The circumstance to check the specified `_right` for.
    \t\t@param _right The right to check for validity.
    \t\t@return true or false, whether user has rights and time is valid.
    \t*/
    \tfunction hasRight (
    \t\taddress _address,
    \t\tbytes32 _circumstance,
    \t\tbytes32 _right
    \t) public view returns (bool) {
    \t\treturn permissions[_address][_circumstance][_right] > block.timestamp;
    \t}
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A migrated ERC-20 BYTES token contract for the Neo Tokyo ecosystem.
    \t@author Tim Clancy <@_Enoch>
    \tThis is the interface for the BYTES 2.0 contract.
    \t@custom:date February 14th, 2023.
    */
    interface IByteContract {
    \t/**
    \t\tPermit authorized callers to burn BYTES from the `_from` address. When 
    \t\tBYTES are burnt, 2/3 of the BYTES are sent to the DAO treasury. This 
    \t\toperation is never expected to overflow given operational bounds on the 
    \t\tamount of BYTES tokens ever allowed to enter circulation.
    \t\t@param _from The address to burn tokens from.
    \t\t@param _amount The amount of tokens to burn.
    \t*/
    \tfunction burn (
    \t\taddress _from,
    \t\tuint256 _amount
    \t) external;
    \t
    \t/**
    \t\tThis function is called by the S1 Citizen contract to emit BYTES to callers 
    \t\tbased on their state from the staker contract.
    \t\t@param _to The reward address to mint BYTES to.
    \t*/
    \tfunction getReward (
    \t\taddress _to
    \t) external;
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity ^0.8.19;
    /**
    \t@custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
    \t@title A generic interface for getting details in the Neo Tokyo ecosystem.
    \t@author Tim Clancy <@_Enoch>
    \t@author Rostislav Khlebnikov <@catpic5buck>
    \tThis is a lazy interface that combines various functions from different 
    \tindependent contracts in the Neo Tokyo ecosystem.
    \t@custom:date February 14th, 2023.
    */
    interface IGenericGetter {
    \t
    \t// S1 Citizen
    \t/**
    \t\tRetrieve the total reward rate of a Neo Tokyo S1 Citizen. This reward rate 
    \t\tis a function of the S1 Citizen's underlying Identity and any optional 
    \t\tVault that has been assembled into the S1 Citizen.
    \t\t@param _citizenId The ID of the S1 Citizen to get a reward rate for. If the 
    \t\t\treward rate is zero, then the S1 Citizen does not exist.
    \t\t@return _ The reward rate of `_citizenId`.
    \t*/
    \tfunction getRewardRateOfTokenId (
    \t\tuint256 _citizenId
    \t) external view returns (uint256);
    \t/**
    \t\tRetrieve the token ID of an S1 Citizen's component Identity.
    \t\t@param _citizenId The ID of the S1 Citizen to get an Identity ID for.
    \t\t@return _ The token ID of the component Identity for `_citizenId`.
    \t*/
    \tfunction getIdentityIdOfTokenId (
    \t\tuint256 _citizenId
    \t) external view returns (uint256);
    \t// S1 Identity
    \t/**
    \t\tRetrieve the class of an S1 Identity.
    \t\t@param _identityId The token ID of the S1 Identity to get the class for.
    \t\t@return _ The class of the Identity with token ID `_identityId`.
    \t*/
    \tfunction getClass (
    \t\tuint256 _identityId
    \t) external view returns (string memory);
    \t// S1 Vault
    \t/**
    \t\tRetrieve the credit multiplier string associated with a particular Vault.
    \t\t@param _vaultId The ID of the Vault to get the credit multiplier for.
    \t\t@return _ The credit multiplier string associated with `_vaultId`.
    \t*/
    \tfunction getCreditMultiplier (
    \t\tuint256 _vaultId
    \t) external view returns (string memory);
    \t// S1 Citizen
    \t/**
    \t\tRetrieve the token ID of a component Vault in a particular S1 Citizen with 
    \t\tthe token ID of `_tokenId`.
    \t\t@param _tokenId The ID of the S1 Citizen to retrieve the Vault token ID for.
    \t\t@return _ The correspnding Vault token ID.
    \t*/
    \tfunction getVaultIdOfTokenId (
    \t\tuint256 _tokenId
    \t) external view returns (uint256);
    }
    pragma solidity ^0.8.19;
    interface INTStakedToken {
        function give(address to, bytes calldata metadata, bytes calldata) external returns(uint256);
        function burn(uint256 tokenId) external returns (uint256);
    }// SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @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.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @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 {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }