APE Price: $0.73 (+0.99%)
    /

    Contract Diff Checker

    Contract Name:
    FactoryErc721a

    Contract Source Code:

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
    
    pragma solidity ^0.8.0;
    
    import "../utils/introspection/IERC165Upgradeable.sol";
    
    /**
     * @dev Interface for the NFT Royalty Standard.
     *
     * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
     * support for royalty payments across all NFT marketplaces and ecosystem participants.
     *
     * _Available since v4.5._
     */
    interface IERC2981Upgradeable is IERC165Upgradeable {
        /**
         * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
         * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
         */
        function royaltyInfo(
            uint256 tokenId,
            uint256 salePrice
        ) external view returns (address receiver, uint256 royaltyAmount);
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
    
    pragma solidity ^0.8.2;
    
    import "../../utils/AddressUpgradeable.sol";
    
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```solidity
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     *
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         * @custom:oz-retyped-from bool
         */
        uint8 private _initialized;
    
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
    
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint8 version);
    
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts.
         *
         * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
         * constructor.
         *
         * Emits an {Initialized} event.
         */
        modifier initializer() {
            bool isTopLevelCall = !_initializing;
            require(
                (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                "Initializable: contract is already initialized"
            );
            _initialized = 1;
            if (isTopLevelCall) {
                _initializing = true;
            }
            _;
            if (isTopLevelCall) {
                _initializing = false;
                emit Initialized(1);
            }
        }
    
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
         * are added through upgrades and that require initialization.
         *
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
         * cannot be nested. If one is invoked in the context of another, execution will revert.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         *
         * WARNING: setting the version to 255 will prevent any future reinitialization.
         *
         * Emits an {Initialized} event.
         */
        modifier reinitializer(uint8 version) {
            require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            _initializing = true;
            _;
            _initializing = false;
            emit Initialized(version);
        }
    
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            require(_initializing, "Initializable: contract is not initializing");
            _;
        }
    
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         *
         * Emits an {Initialized} event the first time it is successfully executed.
         */
        function _disableInitializers() internal virtual {
            require(!_initializing, "Initializable: contract is initializing");
            if (_initialized != type(uint8).max) {
                _initialized = type(uint8).max;
                emit Initialized(type(uint8).max);
            }
        }
    
        /**
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
         */
        function _getInitializedVersion() internal view returns (uint8) {
            return _initialized;
        }
    
        /**
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
         */
        function _isInitializing() internal view returns (bool) {
            return _initializing;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)
    
    pragma solidity ^0.8.0;
    
    import "../../interfaces/IERC2981Upgradeable.sol";
    import "../../utils/introspection/ERC165Upgradeable.sol";
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    
    /**
     * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
     *
     * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
     * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
     *
     * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
     * fee is specified in basis points by default.
     *
     * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
     * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
     * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
     *
     * _Available since v4.5._
     */
    abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
        struct RoyaltyInfo {
            address receiver;
            uint96 royaltyFraction;
        }
    
        RoyaltyInfo private _defaultRoyaltyInfo;
        mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
    
        function __ERC2981_init() internal onlyInitializing {
        }
    
        function __ERC2981_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
            return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
        }
    
        /**
         * @inheritdoc IERC2981Upgradeable
         */
        function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
            RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
    
            if (royalty.receiver == address(0)) {
                royalty = _defaultRoyaltyInfo;
            }
    
            uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
    
            return (royalty.receiver, royaltyAmount);
        }
    
        /**
         * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
         * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
         * override.
         */
        function _feeDenominator() internal pure virtual returns (uint96) {
            return 10000;
        }
    
        /**
         * @dev Sets the royalty information that all ids in this contract will default to.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: invalid receiver");
    
            _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
        }
    
        /**
         * @dev Removes default royalty information.
         */
        function _deleteDefaultRoyalty() internal virtual {
            delete _defaultRoyaltyInfo;
        }
    
        /**
         * @dev Sets the royalty information for a specific token id, overriding the global default.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: Invalid parameters");
    
            _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
        }
    
        /**
         * @dev Resets royalty information for the token id back to the global default.
         */
        function _resetTokenRoyalty(uint256 tokenId) internal virtual {
            delete _tokenRoyaltyInfo[tokenId];
        }
    
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[48] private __gap;
    }

    // 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 AddressUpgradeable {
        /**
         * @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/introspection/ERC165.sol)
    
    pragma solidity ^0.8.0;
    
    import "./IERC165Upgradeable.sol";
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
        function __ERC165_init() internal onlyInitializing {
        }
    
        function __ERC165_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165Upgradeable).interfaceId;
        }
    
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165Upgradeable {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }

    // 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) (proxy/Clones.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
     * deploying minimal proxy contracts, also known as "clones".
     *
     * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
     * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
     *
     * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
     * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
     * deterministic method.
     *
     * _Available since v3.4._
     */
    library Clones {
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
         *
         * This function uses the create opcode, which should never revert.
         */
        function clone(address implementation) internal returns (address instance) {
            /// @solidity memory-safe-assembly
            assembly {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create(0, 0x09, 0x37)
            }
            require(instance != address(0), "ERC1167: create failed");
        }
    
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy
         * the clone. Using the same `implementation` and `salt` multiple time will revert, since
         * the clones cannot be deployed twice at the same address.
         */
        function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
            /// @solidity memory-safe-assembly
            assembly {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create2(0, 0x09, 0x37, salt)
            }
            require(instance != address(0), "ERC1167: create2 failed");
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt,
            address deployer
        ) internal pure returns (address predicted) {
            /// @solidity memory-safe-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(add(ptr, 0x38), deployer)
                mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                mstore(add(ptr, 0x14), implementation)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                mstore(add(ptr, 0x58), salt)
                mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                predicted := keccak256(add(ptr, 0x43), 0x55)
            }
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt
        ) internal view returns (address predicted) {
            return predictDeterministicAddress(implementation, salt, address(this));
        }
    }

    // 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: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
    
    pragma solidity ^0.8.0;
    
    import "../../utils/introspection/IERC165.sol";
    
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
    
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
    
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId) external;
    
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
         * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
         * understand this adds an external call which potentially creates a reentrancy vulnerability.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 tokenId) external;
    
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
    
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool approved) external;
    
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
    
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (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;
        }
    
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }

    // SPDX-License-Identifier: MIT
    // ArchetypePayouts v0.7.0
    //
    //        d8888                 888               888
    //       d88888                 888               888
    //      d88P888                 888               888
    //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
    //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
    //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
    //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
    // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
    //                                                            888 888
    //                                                       Y8b d88P 888
    //
    
    pragma solidity ^0.8.4;
    
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    error InvalidLength();
    error InvalidSplitShares();
    error TransferFailed();
    error BalanceEmpty();
    error NotApprovedToWithdraw();
    
    contract ArchetypePayouts {
      event Withdrawal(address indexed src, address token, uint256 wad);
      event FundsAdded(address indexed recipient, address token, uint256 amount);
    
      mapping(address => mapping(address => uint256)) private _balance;
      mapping(address => mapping(address => bool)) private _approvals;
    
      function updateBalances(
        uint256 totalAmount,
        address token,
        address[] calldata recipients,
        uint16[] calldata splits
      ) public payable {
        if (recipients.length != splits.length) {
          revert InvalidLength();
        }
    
        uint256 totalShares = 0;
        for (uint256 i = 0; i < splits.length; i++) {
          totalShares += splits[i];
        }
        if (totalShares != 10000) {
          revert InvalidSplitShares();
        }
    
        if (token == address(0)) {
          // ETH payments
          uint256 totalReceived = msg.value;
          for (uint256 i = 0; i < recipients.length; i++) {
            if (splits[i] > 0) {
              uint256 amountToAdd = (totalReceived * splits[i]) / 10000;
              _balance[recipients[i]][token] += amountToAdd;
              emit FundsAdded(recipients[i], token, amountToAdd);
            }
          }
        } else {
          // ERC20 payments
          IERC20 paymentToken = IERC20(token);
          bool success = paymentToken.transferFrom(msg.sender, address(this), totalAmount);
          if (!success) {
            revert TransferFailed();
          }
    
          for (uint256 i = 0; i < recipients.length; i++) {
            if (splits[i] > 0) {
              uint256 amountToAdd = (totalAmount * splits[i]) / 10000;
              _balance[recipients[i]][token] += amountToAdd;
              emit FundsAdded(recipients[i], token, amountToAdd);
            }
          }
        }
      }
    
      function withdraw() external {
        address msgSender = msg.sender;
        _withdraw(msgSender, msgSender, address(0));
      }
    
      function withdrawTokens(address[] memory tokens) external {
        address msgSender = msg.sender;
    
        for (uint256 i = 0; i < tokens.length; i++) {
          _withdraw(msgSender, msgSender, tokens[i]);
        }
      }
    
      function withdrawFrom(address from, address to) public {
        if (from != msg.sender && !_approvals[from][to]) {
          revert NotApprovedToWithdraw();
        }
        _withdraw(from, to, address(0));
      }
    
      function withdrawTokensFrom(
        address from,
        address to,
        address[] memory tokens
      ) public {
        if (from != msg.sender && !_approvals[from][to]) {
          revert NotApprovedToWithdraw();
        }
        for (uint256 i = 0; i < tokens.length; i++) {
          _withdraw(from, to, tokens[i]);
        }
      }
    
      function _withdraw(
        address from,
        address to,
        address token
      ) internal {
        uint256 wad;
    
        wad = _balance[from][token];
        _balance[from][token] = 0;
    
        if (wad == 0) {
          revert BalanceEmpty();
        }
    
        if (token == address(0)) {
          bool success = false;
          (success, ) = to.call{ value: wad }("");
          if (!success) {
            revert TransferFailed();
          }
        } else {
          IERC20 erc20Token = IERC20(token);
          bool success = erc20Token.transfer(to, wad);
          if (!success) {
            revert TransferFailed();
          }
        }
        emit Withdrawal(from, token, wad);
      }
    
      function approveWithdrawal(address delegate, bool approved) external {
        _approvals[msg.sender][delegate] = approved;
      }
    
      function isApproved(address from, address delegate) external view returns (bool) {
        return _approvals[from][delegate];
      }
    
      function balance(address recipient) external view returns (uint256) {
        return _balance[recipient][address(0)];
      }
    
      function balanceToken(address recipient, address token) external view returns (uint256) {
        return _balance[recipient][token];
      }
    }

    // SPDX-License-Identifier: MIT
    // Archetype v0.8.0
    //
    //        d8888                 888               888
    //       d88888                 888               888
    //      d88P888                 888               888
    //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
    //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
    //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
    //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
    // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
    //                                                            888 888
    //                                                       Y8b d88P 888
    //                                                        "Y88P"  888
    
    pragma solidity ^0.8.20;
    
    import "./ArchetypeLogicErc721a.sol";
    import "erc721a-upgradeable/contracts/ERC721AUpgradeable.sol";
    import "erc721a-upgradeable/contracts/ERC721A__Initializable.sol";
    import "erc721a-upgradeable/contracts/extensions/ERC721AQueryableUpgradeable.sol";
    import "./ERC721A__OwnableUpgradeable.sol";
    import "solady/src/utils/LibString.sol";
    import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
    
    contract ArchetypeErc721a is
      ERC721A__Initializable,
      ERC721AUpgradeable,
      ERC721A__OwnableUpgradeable,
      ERC2981Upgradeable,
      ERC721AQueryableUpgradeable
    {
      //
      // EVENTS
      //
      event Invited(bytes32 indexed key, bytes32 indexed cid);
      event BurnInvited(bytes32 indexed key, bytes32 indexed cid);
      event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
      event Withdrawal(address indexed src, address token, uint128 wad);
    
      //
      // VARIABLES
      //
      mapping(bytes32 => AdvancedInvite) public invites;
      mapping(bytes32 => uint256) public packedBonusDiscounts;
      mapping(bytes32 => BurnInvite) public burnInvites;
      mapping(address => mapping(bytes32 => uint256)) private _minted;
      mapping(bytes32 => uint256) private _listSupply;
      mapping(address => uint128) private _ownerBalance;
      mapping(address => mapping(address => uint128)) private _affiliateBalance;
    
      Config public config;
      PayoutConfig public payoutConfig;
      Options public options;
    
      //
      // METHODS
      //
      function initialize(
        string memory name,
        string memory symbol,
        Config calldata config_,
        PayoutConfig calldata payoutConfig_,
        address _receiver
      ) external initializerERC721A {
        __ERC721A_init(name, symbol);
        // check max bps not reached and min platform fee.
        if (
          config_.affiliateFee > MAXBPS ||
          config_.affiliateDiscount > MAXBPS ||
          config_.affiliateSigner == address(0) ||
          config_.maxBatchSize == 0
        ) {
          revert InvalidConfig();
        }
        config = config_;
        __Ownable_init();
    
        uint256 totalShares = payoutConfig_.ownerBps +
          payoutConfig_.platformBps +
          payoutConfig_.partnerBps +
          payoutConfig_.superAffiliateBps;
    
        if (payoutConfig_.platformBps < 250 || totalShares != 10000) {
          revert InvalidSplitShares();
        }
        payoutConfig = payoutConfig_;
        setDefaultRoyalty(_receiver, config.defaultRoyalty);
      }
    
      //
      // PUBLIC
      //
      function mint(
        Auth calldata auth,
        uint256 quantity,
        address affiliate,
        bytes calldata signature
      ) external payable {
        mintTo(auth, quantity, _msgSender(), affiliate, signature);
      }
    
      function batchMintTo(
        Auth calldata auth,
        address[] calldata toList,
        uint256[] calldata quantityList,
        address affiliate,
        bytes calldata signature
      ) external payable {
        if (quantityList.length != toList.length) {
          revert InvalidConfig();
        }
    
        AdvancedInvite storage invite = invites[auth.key];
        uint256 packedDiscount = packedBonusDiscounts[auth.key];
        uint256 curSupply = _totalMinted();
        
        uint256 totalQuantity;
        uint256 totalBonusMints;
    
        for (uint256 i; i < toList.length; ) {
          uint256 quantityToAdd;
          if (invite.unitSize > 1) {
            quantityToAdd = quantityList[i] * invite.unitSize;
          } else {
            quantityToAdd = quantityList[i];
          }
    
          uint256 numBonusMints = ArchetypeLogicErc721a.bonusMintsAwarded(quantityToAdd, packedDiscount);
          _mint(toList[i], quantityToAdd + numBonusMints);
    
          totalQuantity += quantityToAdd;
          totalBonusMints += numBonusMints;
    
          unchecked {
            ++i;
          }
        }
    
        validateAndCreditMint(invite, auth, totalQuantity, totalBonusMints, curSupply, affiliate, signature);
      }
    
      function mintTo(
        Auth calldata auth,
        uint256 quantity,
        address to,
        address affiliate,
        bytes calldata signature
      ) public payable {
        AdvancedInvite storage invite = invites[auth.key];
        uint256 packedDiscount = packedBonusDiscounts[auth.key];
    
        if (invite.unitSize > 1) {
          quantity = quantity * invite.unitSize;
        }
    
        uint256 curSupply = _totalMinted();
    
        uint256 numBonusMints = ArchetypeLogicErc721a.bonusMintsAwarded(quantity, packedDiscount);
        _mint(to, quantity + numBonusMints);
    
        validateAndCreditMint(invite, auth, quantity, numBonusMints, curSupply, affiliate, signature);
      }
    
      function validateAndCreditMint(
        AdvancedInvite storage invite,
        Auth calldata auth,
        uint256 quantity,
        uint256 numBonusMints,
        uint256 curSupply,
        address affiliate,
        bytes calldata signature
      ) internal {
        uint256 totalQuantity = quantity + numBonusMints;
        ValidationArgs memory args;
        {
          args = ValidationArgs({
            owner: owner(),
            affiliate: affiliate,
            quantity: totalQuantity,
            curSupply: curSupply,
            listSupply: _listSupply[auth.key]
          });
        }
    
        uint128 cost = uint128(
          ArchetypeLogicErc721a.computePrice(
            invite,
            config.affiliateDiscount,
            quantity,
            args.listSupply,
            args.affiliate != address(0)
          )
        );
    
        ArchetypeLogicErc721a.validateMint(invite, config, auth, _minted, signature, args, cost);
    
        if (invite.limit < invite.maxSupply) {
          _minted[_msgSender()][auth.key] += totalQuantity;
        }
        if (invite.maxSupply < UINT32_MAX) {
          _listSupply[auth.key] += totalQuantity;
        }
    
        ArchetypeLogicErc721a.updateBalances(
          invite.tokenAddress,
          config,
          _ownerBalance,
          _affiliateBalance,
          affiliate,
          quantity,
          cost
        );
    
        if (msg.value > cost) {
          _refund(_msgSender(), msg.value - cost);
        }
      }
    
      function burnToMint(Auth calldata auth, uint256[] calldata tokenIds) external payable {
        BurnInvite storage burnInvite = burnInvites[auth.key];
    
        uint256 curSupply = _totalMinted();
        uint128 cost = burnInvite.price;
        ArchetypeLogicErc721a.validateBurnToMint(burnInvite, config, auth, tokenIds, curSupply, _minted, cost);
    
        address msgSender = _msgSender();
        for (uint256 i; i < tokenIds.length; ) {
          address burnAddress = burnInvite.burnAddress != address(0)
            ? burnInvite.burnAddress
            : address(0x000000000000000000000000000000000000dEaD);
          burnInvite.burnErc721.transferFrom(msgSender, burnAddress, tokenIds[i]);
          unchecked {
            ++i;
          }
        }
    
        uint256 quantity = burnInvite.reversed
          ? tokenIds.length * burnInvite.ratio
          : tokenIds.length / burnInvite.ratio;
        _mint(msgSender, quantity);
    
        if (burnInvite.limit < config.maxSupply) {
          _minted[msgSender][keccak256(abi.encodePacked("burn", auth.key))] += quantity;
        }
    
        ArchetypeLogicErc721a.updateBalances(
          burnInvite.tokenAddress,
          config,
          _ownerBalance,
          _affiliateBalance,
          address(0), // burn to mint does not support affiliates
          quantity,
          cost
        );
    
        if (msg.value > cost) {
          _refund(_msgSender(), msg.value - cost);
        }
      }
    
      function tokenURI(uint256 tokenId) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
    
        return
          bytes(config.baseUri).length != 0
            ? string(abi.encodePacked(config.baseUri, LibString.toString(tokenId)))
            : "";
      }
    
      function withdraw() external {
        address[] memory tokens = new address[](1);
        tokens[0] = address(0);
        withdrawTokens(tokens);
      }
    
      function withdrawTokens(address[] memory tokens) public {
        ArchetypeLogicErc721a.withdrawTokens(payoutConfig, _ownerBalance, owner(), tokens);
      }
    
      function withdrawAffiliate() external {
        address[] memory tokens = new address[](1);
        tokens[0] = address(0);
        withdrawTokensAffiliate(tokens);
      }
    
      function withdrawTokensAffiliate(address[] memory tokens) public {
        ArchetypeLogicErc721a.withdrawTokensAffiliate(_affiliateBalance, tokens);
      }
    
      function ownerBalance() external view returns (uint128) {
        return _ownerBalance[address(0)];
      }
    
      function ownerBalanceToken(address token) external view returns (uint128) {
        return _ownerBalance[token];
      }
    
      function affiliateBalance(address affiliate) external view returns (uint128) {
        return _affiliateBalance[affiliate][address(0)];
      }
    
      function affiliateBalanceToken(address affiliate, address token) external view returns (uint128) {
        return _affiliateBalance[affiliate][token];
      }
    
      function minted(address minter, bytes32 key) external view returns (uint256) {
        return _minted[minter][key];
      }
    
      function listSupply(bytes32 key) external view returns (uint256) {
        return _listSupply[key];
      }
    
      function platform() external pure returns (address) {
        return PLATFORM;
      }
    
      function computePrice(
        bytes32 key,
        uint256 quantity,
        bool affiliateUsed
      ) external view returns (uint256) {
        AdvancedInvite storage i = invites[key];
        uint256 listSupply_ = _listSupply[key];
        return ArchetypeLogicErc721a.computePrice(i, config.affiliateDiscount, quantity, listSupply_, affiliateUsed);
      }
    
      //
      // OWNER ONLY
      //
    
      function setBaseURI(string memory baseUri) external _onlyOwner {
        if (options.uriLocked) {
          revert LockedForever();
        }
    
        config.baseUri = baseUri;
      }
    
      /// @notice the password is "forever"
      function lockURI(string memory password) external _onlyOwner {
        if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
          revert WrongPassword();
        }
    
        options.uriLocked = true;
      }
    
      /// @notice the password is "forever"
      // max supply cannot subceed total supply. Be careful changing.
      function setMaxSupply(uint32 maxSupply, string memory password) external _onlyOwner {
        if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
          revert WrongPassword();
        }
    
        if (options.maxSupplyLocked) {
          revert LockedForever();
        }
    
        if (maxSupply < _totalMinted()) {
          revert MaxSupplyExceeded();
        }
    
        config.maxSupply = maxSupply;
      }
    
      /// @notice the password is "forever"
      function lockMaxSupply(string memory password) external _onlyOwner {
        if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
          revert WrongPassword();
        }
    
        options.maxSupplyLocked = true;
      }
    
      function setAffiliateFee(uint16 affiliateFee) external _onlyOwner {
        if (options.affiliateFeeLocked) {
          revert LockedForever();
        }
        if (affiliateFee > MAXBPS) {
          revert InvalidConfig();
        }
    
        config.affiliateFee = affiliateFee;
      }
    
      function setAffiliateDiscount(uint16 affiliateDiscount) external _onlyOwner {
        if (options.affiliateFeeLocked) {
          revert LockedForever();
        }
        if (affiliateDiscount > MAXBPS) {
          revert InvalidConfig();
        }
    
        config.affiliateDiscount = affiliateDiscount;
      }
    
      /// @notice the password is "forever"
      function lockAffiliateFee(string memory password) external _onlyOwner {
        if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
          revert WrongPassword();
        }
    
        options.affiliateFeeLocked = true;
      }
    
      function setOwnerAltPayout(address ownerAltPayout) external _onlyOwner {
        if (options.ownerAltPayoutLocked) {
          revert LockedForever();
        }
    
        payoutConfig.ownerAltPayout = ownerAltPayout;
      }
    
      function lockOwnerAltPayout() external _onlyOwner {
        options.ownerAltPayoutLocked = true;
      }
    
      function setMaxBatchSize(uint32 maxBatchSize) external _onlyOwner {
        config.maxBatchSize = maxBatchSize;
      }
    
      function setBonusDiscounts(bytes32 _key, BonusDiscount[] calldata _bonusDiscounts) public onlyOwner {
        if(_bonusDiscounts.length > 8) {
          revert InvalidConfig();
        }
    
        uint256 packed;
        for (uint8 i = 0; i < _bonusDiscounts.length; i++) {
            if (i > 0 && _bonusDiscounts[i].numMints >= _bonusDiscounts[i - 1].numMints) {
                revert InvalidConfig();
            }
            uint32 discount = (uint32(_bonusDiscounts[i].numMints) << 16) | uint32(_bonusDiscounts[i].numBonusMints);
            packed |= uint256(discount) << (32 * i);
        }
        packedBonusDiscounts[_key] = packed;
      }
    
      function setBonusInvite(
        bytes32 _key,
        bytes32 _cid,
        AdvancedInvite calldata _advancedInvite,
        BonusDiscount[] calldata _bonusDiscount
      ) external _onlyOwner {
        setBonusDiscounts(_key, _bonusDiscount);
        setAdvancedInvite(_key, _cid, _advancedInvite);
      }
    
      function setInvite(
        bytes32 _key,
        bytes32 _cid,
        Invite calldata _invite
      ) external _onlyOwner {
        setAdvancedInvite(_key, _cid, AdvancedInvite({
          price: _invite.price,
          reservePrice: _invite.price,
          delta: 0,
          start: _invite.start,
          end: _invite.end,
          limit: _invite.limit,
          maxSupply: _invite.maxSupply,
          interval: 0,
          unitSize: _invite.unitSize,
          tokenAddress: _invite.tokenAddress,
          isBlacklist: _invite.isBlacklist
        }));
      }
    
      function setAdvancedInvite(
        bytes32 _key,
        bytes32 _cid,
        AdvancedInvite memory _AdvancedInvite
      ) public _onlyOwner {
        // approve token for withdrawals if erc20 list
        if (_AdvancedInvite.tokenAddress != address(0)) {
          bool success = IERC20(_AdvancedInvite.tokenAddress).approve(PAYOUTS, 2**256 - 1);
          if (!success) {
            revert NotApprovedToTransfer();
          }
        }
        if (_AdvancedInvite.start < block.timestamp) {
          _AdvancedInvite.start = uint32(block.timestamp);
        }
        invites[_key] = _AdvancedInvite;
        emit Invited(_key, _cid);
      }
    
      function setBurnInvite(
        bytes32 _key,
        bytes32 _cid,
        BurnInvite memory _burnInvite
      ) external _onlyOwner {
        if (_burnInvite.start < block.timestamp) {
          _burnInvite.start = uint32(block.timestamp);
        }
        burnInvites[_key] = _burnInvite;
        emit BurnInvited(_key, _cid);
      }
    
      //
      // INTERNAL
      //
      function _startTokenId() internal view virtual override returns (uint256) {
        return 1;
      }
    
      function _msgSender() internal view returns (address) {
        return msg.sender == BATCH ? tx.origin : msg.sender;
      }
    
      modifier _onlyPlatform() {
        if (_msgSender() != PLATFORM) {
          revert NotPlatform();
        }
        _;
      }
    
      modifier _onlyOwner() {
        if (_msgSender() != owner()) {
          revert NotOwner();
        }
        _;
      }
    
      function _refund(address to, uint256 refund) internal {
        (bool success, ) = payable(to).call{ value: refund }("");
        if (!success) {
          revert TransferFailed();
        }
      }
    
      //ERC2981 ROYALTY
      function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(IERC721AUpgradeable, ERC721AUpgradeable, ERC2981Upgradeable)
        returns (bool)
      {
        // Supports the following `interfaceId`s:
        // - IERC165: 0x01ffc9a7
        // - IERC721: 0x80ac58cd
        // - IERC721Metadata: 0x5b5e139f
        // - IERC2981: 0x2a55205a
        return
          ERC721AUpgradeable.supportsInterface(interfaceId) ||
          ERC2981Upgradeable.supportsInterface(interfaceId);
      }
    
      function setDefaultRoyalty(address receiver, uint16 feeNumerator) public _onlyOwner {
        config.defaultRoyalty = feeNumerator;
        _setDefaultRoyalty(receiver, feeNumerator);
      }
    }

    // SPDX-License-Identifier: MIT
    // ArchetypeLogic v0.8.0
    //
    //        d8888                 888               888
    //       d88888                 888               888
    //      d88P888                 888               888
    //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
    //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
    //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
    //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
    // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
    //                                                            888 888
    //                                                       Y8b d88P 888
    //                                                        "Y88P"  888
    
    pragma solidity ^0.8.20;
    
    import "../ArchetypePayouts.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "solady/src/utils/MerkleProofLib.sol";
    import "solady/src/utils/ECDSA.sol";
    
    error InvalidConfig();
    error MintNotYetStarted();
    error MintEnded();
    error WalletUnauthorizedToMint();
    error InsufficientEthSent();
    error ExcessiveEthSent();
    error Erc20BalanceTooLow();
    error MaxSupplyExceeded();
    error ListMaxSupplyExceeded();
    error NumberOfMintsExceeded();
    error MintingPaused();
    error InvalidReferral();
    error InvalidSignature();
    error MaxBatchSizeExceeded();
    error BurnToMintDisabled();
    error NotTokenOwner();
    error NotPlatform();
    error NotOwner();
    error NotShareholder();
    error NotApprovedToTransfer();
    error InvalidAmountOfTokens();
    error WrongPassword();
    error LockedForever();
    error Blacklisted();
    
    //
    // STRUCTS
    //
    struct Auth {
      bytes32 key;
      bytes32[] proof;
    }
    
    struct BonusDiscount {
      uint16 numMints;
      uint16 numBonusMints;
    }
    
    struct Config {
      string baseUri;
      address affiliateSigner;
      uint32 maxSupply;
      uint32 maxBatchSize;
      uint16 affiliateFee; //BPS
      uint16 affiliateDiscount; //BPS
      uint16 defaultRoyalty; //BPS
    }
    
    // allocation splits for withdrawn owner funds, must sum to 100%
    struct PayoutConfig {
      uint16 ownerBps;
      uint16 platformBps;
      uint16 partnerBps;
      uint16 superAffiliateBps;
      address partner;
      address superAffiliate;
      address ownerAltPayout;
    }
    
    struct Options {
      bool uriLocked;
      bool maxSupplyLocked;
      bool affiliateFeeLocked;
      bool ownerAltPayoutLocked;
    }
    
    struct AdvancedInvite {
      uint128 price;
      uint128 reservePrice;
      uint128 delta;
      uint32 start;
      uint32 end;
      uint32 limit;
      uint32 maxSupply;
      uint32 interval;
      uint32 unitSize; // mint 1 get x
      address tokenAddress;
      bool isBlacklist;
    }
    
    struct Invite {
      uint128 price;
      uint32 start;
      uint32 end;
      uint32 limit;
      uint32 maxSupply;
      uint32 unitSize; // mint 1 get x
      address tokenAddress;
      bool isBlacklist;
    }
    
    struct BurnInvite {
      IERC721 burnErc721;
      address burnAddress;
      address tokenAddress;
      uint128 price; // flat price - does not support discounts
      bool reversed; // side of the ratio (false=burn {ratio} get 1, true=burn 1 get {ratio})
      uint16 ratio;
      uint32 start;
      uint32 end;
      uint64 limit;
    }
    
    struct ValidationArgs {
      address owner;
      address affiliate;
      uint256 quantity;
      uint256 curSupply;
      uint256 listSupply;
    }
    
    // UPDATE CONSTANTS BEFORE DEPLOY
    address constant PLATFORM = 0x8952caF7E5bf1fe63ebe94148ca802F3eF127C98;
    address constant BATCH = 0xEa49e7bE310716dA66725c84a5127d2F6A202eAf;
    address constant PAYOUTS = 0xaAfdfA4a935d8511bF285af11A0544ce7e4a1199;
    uint16 constant MAXBPS = 5000; // max fee or discount is 50%
    uint32 constant UINT32_MAX = 2**32 - 1;
    
    library ArchetypeLogicErc721a {
      //
      // EVENTS
      //
      event Invited(bytes32 indexed key, bytes32 indexed cid);
      event BurnInvited(bytes32 indexed key, bytes32 indexed cid);
      event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
      event Withdrawal(address indexed src, address token, uint128 wad);
    
      // calculate price based on affiliate usage and mint discounts
      function computePrice(
        AdvancedInvite storage invite,
        uint16 affiliateDiscount,
        uint256 numTokens,
        uint256 listSupply,
        bool affiliateUsed
      ) public view returns (uint256) {
        uint256 price = invite.price;
        uint256 cost;
        if (invite.interval > 0 && invite.delta > 0) {
          // Apply dutch pricing
          uint256 diff = (((block.timestamp - invite.start) / invite.interval) * invite.delta);
          if (price > invite.reservePrice) {
            if (diff > price - invite.reservePrice) {
              price = invite.reservePrice;
            } else {
              price = price - diff;
            }
          } else if (price < invite.reservePrice) {
            if (diff > invite.reservePrice - price) {
              price = invite.reservePrice;
            } else {
              price = price + diff;
            }
          }
          cost = price * numTokens;
        } else if (invite.interval == 0 && invite.delta > 0) {
          // Apply linear curve
          uint256 lastPrice = price + invite.delta * listSupply;
          cost = lastPrice * numTokens + (invite.delta * numTokens * (numTokens - 1)) / 2;
        } else {
          cost = price * numTokens;
        }
    
        if (affiliateUsed) {
          cost = cost - ((cost * affiliateDiscount) / 10000);
        }
    
        return cost;
      }
    
      function bonusMintsAwarded(uint256 numNfts, uint256 packedDiscount) internal pure returns (uint256) {
        for (uint8 i = 0; i < 8; i++) {
            uint32 discount = uint32((packedDiscount >> (32 * i)) & 0xFFFFFFFF);
            uint16 tierNumMints = uint16(discount >> 16);
            uint16 tierBonusMints = uint16(discount);
    
            if (tierNumMints == 0) {
                break; // End of valid discounts
            }
    
            if (numNfts >= tierNumMints) {
                return (numNfts / tierNumMints) * tierBonusMints;
            }
        }
        return 0;
      }
    
      function validateMint(
        AdvancedInvite storage i,
        Config storage config,
        Auth calldata auth,
        mapping(address => mapping(bytes32 => uint256)) storage minted,
        bytes calldata signature,
        ValidationArgs memory args,
        uint128 cost
      ) public view {
        address msgSender = _msgSender();
        if (args.affiliate != address(0)) {
          if (
            args.affiliate == PLATFORM || args.affiliate == args.owner || args.affiliate == msgSender
          ) {
            revert InvalidReferral();
          }
          validateAffiliate(args.affiliate, signature, config.affiliateSigner);
        }
    
        if (i.limit == 0) {
          revert MintingPaused();
        }
    
        if (!i.isBlacklist) {
          if (!verify(auth, i.tokenAddress, msgSender)) {
            revert WalletUnauthorizedToMint();
          }
        } else {
          if (verify(auth, i.tokenAddress, msgSender)) {
            revert Blacklisted();
          }
        }
    
        if (block.timestamp < i.start) {
          revert MintNotYetStarted();
        }
    
        if (i.end > i.start && block.timestamp > i.end) {
          revert MintEnded();
        }
    
        if (i.limit < i.maxSupply) {
          uint256 totalAfterMint = minted[msgSender][auth.key] + args.quantity;
    
          if (totalAfterMint > i.limit) {
            revert NumberOfMintsExceeded();
          }
        }
    
        if (i.maxSupply < config.maxSupply) {
          uint256 totalAfterMint = args.listSupply + args.quantity;
          if (totalAfterMint > i.maxSupply) {
            revert ListMaxSupplyExceeded();
          }
        }
    
        if (args.quantity > config.maxBatchSize) {
          revert MaxBatchSizeExceeded();
        }
    
        if ((args.curSupply + args.quantity) > config.maxSupply) {
          revert MaxSupplyExceeded();
        }
    
        if (i.tokenAddress != address(0)) {
          IERC20 erc20Token = IERC20(i.tokenAddress);
          if (erc20Token.allowance(msgSender, address(this)) < cost) {
            revert NotApprovedToTransfer();
          }
    
          if (erc20Token.balanceOf(msgSender) < cost) {
            revert Erc20BalanceTooLow();
          }
    
          if (msg.value != 0) {
            revert ExcessiveEthSent();
          }
        } else {
          if (msg.value < cost) {
            revert InsufficientEthSent();
          }
        }
      }
    
      function validateBurnToMint(
        BurnInvite storage burnInvite,
        Config storage config,
        Auth calldata auth,
        uint256[] calldata tokenIds,
        uint256 curSupply,
        mapping(address => mapping(bytes32 => uint256)) storage minted,
        uint128 cost
      ) public view {
        if (burnInvite.limit == 0) {
          revert MintingPaused();
        }
    
        if (block.timestamp < burnInvite.start) {
          revert MintNotYetStarted();
        }
    
        if (burnInvite.end > burnInvite.start && block.timestamp > burnInvite.end) {
          revert MintEnded();
        }
    
        // check if msgSender owns tokens and has correct approvals
        address msgSender = _msgSender();
        for (uint256 i; i < tokenIds.length; ) {
          if (burnInvite.burnErc721.ownerOf(tokenIds[i]) != msgSender) {
            revert NotTokenOwner();
          }
          unchecked {
            ++i;
          }
        }
    
        if (!verify(auth, burnInvite.tokenAddress, msgSender)) {
          revert WalletUnauthorizedToMint();
        }
    
        if (!burnInvite.burnErc721.isApprovedForAll(msgSender, address(this))) {
          revert NotApprovedToTransfer();
        }
    
        uint256 quantity;
        if (burnInvite.reversed) {
          quantity = tokenIds.length * burnInvite.ratio;
        } else {
          if (tokenIds.length % burnInvite.ratio != 0) {
            revert InvalidAmountOfTokens();
          }
          quantity = tokenIds.length / burnInvite.ratio;
        }
    
        if (quantity > config.maxBatchSize) {
          revert MaxBatchSizeExceeded();
        }
    
        if (burnInvite.limit < config.maxSupply) {
          uint256 totalAfterMint = minted[msgSender][keccak256(abi.encodePacked("burn", auth.key))] +
            quantity;
    
          if (totalAfterMint > burnInvite.limit) {
            revert NumberOfMintsExceeded();
          }
        }
    
        if ((curSupply + quantity) > config.maxSupply) {
          revert MaxSupplyExceeded();
        }
    
        if (burnInvite.tokenAddress != address(0)) {
          IERC20 erc20Token = IERC20(burnInvite.tokenAddress);
          if (erc20Token.allowance(msgSender, address(this)) < cost) {
            revert NotApprovedToTransfer();
          }
    
          if (erc20Token.balanceOf(msgSender) < cost) {
            revert Erc20BalanceTooLow();
          }
    
          if (msg.value != 0) {
            revert ExcessiveEthSent();
          }
        } else {
          if (msg.value < cost) {
            revert InsufficientEthSent();
          }
        }
      }
    
      function updateBalances(
        address tokenAddress,
        Config storage config,
        mapping(address => uint128) storage _ownerBalance,
        mapping(address => mapping(address => uint128)) storage _affiliateBalance,
        address affiliate,
        uint256 quantity,
        uint128 value
      ) public {
        uint128 affiliateWad;
        if (affiliate != address(0)) {
          affiliateWad = (value * config.affiliateFee) / 10000;
          _affiliateBalance[affiliate][tokenAddress] += affiliateWad;
          emit Referral(affiliate, tokenAddress, affiliateWad, quantity);
        }
    
        uint128 balance = _ownerBalance[tokenAddress];
        uint128 ownerWad = value - affiliateWad;
        _ownerBalance[tokenAddress] = balance + ownerWad;
    
        if (tokenAddress != address(0)) {
          IERC20 erc20Token = IERC20(tokenAddress);
          bool success = erc20Token.transferFrom(_msgSender(), address(this), value);
          if (!success) {
            revert TransferFailed();
          }
        }
      }
    
      function withdrawTokensAffiliate(
        mapping(address => mapping(address => uint128)) storage _affiliateBalance,
        address[] calldata tokens
      ) public {
        address msgSender = _msgSender();
    
        for (uint256 i; i < tokens.length; i++) {
          address tokenAddress = tokens[i];
          uint128 wad = _affiliateBalance[msgSender][tokenAddress];
          _affiliateBalance[msgSender][tokenAddress] = 0;
    
          if (wad == 0) {
            revert BalanceEmpty();
          }
    
          if (tokenAddress == address(0)) {
            bool success = false;
            (success, ) = msgSender.call{ value: wad }("");
            if (!success) {
              revert TransferFailed();
            }
          } else {
            IERC20 erc20Token = IERC20(tokenAddress);
            bool success = erc20Token.transfer(msgSender, wad);
            if (!success) {
              revert TransferFailed();
            }
          }
    
          emit Withdrawal(msgSender, tokenAddress, wad);
        }
      }
    
      function withdrawTokens(
        PayoutConfig storage payoutConfig,
        mapping(address => uint128) storage _ownerBalance,
        address owner,
        address[] calldata tokens
      ) public {
        address msgSender = _msgSender();
        for (uint256 i; i < tokens.length; i++) {
          address tokenAddress = tokens[i];
          uint128 wad;
    
          if (
            msgSender == owner ||
            msgSender == PLATFORM ||
            msgSender == payoutConfig.partner ||
            msgSender == payoutConfig.superAffiliate ||
            msgSender == payoutConfig.ownerAltPayout
          ) {
            wad = _ownerBalance[tokenAddress];
            _ownerBalance[tokenAddress] = 0;
          } else {
            revert NotShareholder();
          }
    
          if (wad == 0) {
            revert BalanceEmpty();
          }
    
          if (payoutConfig.ownerAltPayout == address(0)) {
            address[] memory recipients = new address[](4);
            recipients[0] = owner;
            recipients[1] = PLATFORM;
            recipients[2] = payoutConfig.partner;
            recipients[3] = payoutConfig.superAffiliate;
    
            uint16[] memory splits = new uint16[](4);
            splits[0] = payoutConfig.ownerBps;
            splits[1] = payoutConfig.platformBps;
            splits[2] = payoutConfig.partnerBps;
            splits[3] = payoutConfig.superAffiliateBps;
    
            if (tokenAddress == address(0)) {
              ArchetypePayouts(PAYOUTS).updateBalances{ value: wad }(
                wad,
                tokenAddress,
                recipients,
                splits
              );
            } else {
              ArchetypePayouts(PAYOUTS).updateBalances(wad, tokenAddress, recipients, splits);
            }
          } else {
            uint256 ownerShare = (uint256(wad) * payoutConfig.ownerBps) / 10000;
            uint256 remainingShare = wad - ownerShare;
    
            if (tokenAddress == address(0)) {
              (bool success, ) = payable(payoutConfig.ownerAltPayout).call{ value: ownerShare }("");
              if (!success) revert TransferFailed();
            } else {
              IERC20(tokenAddress).transfer(payoutConfig.ownerAltPayout, ownerShare);
            }
    
            address[] memory recipients = new address[](3);
            recipients[0] = PLATFORM;
            recipients[1] = payoutConfig.partner;
            recipients[2] = payoutConfig.superAffiliate;
    
            uint16[] memory splits = new uint16[](3);
            uint16 remainingBps = 10000 - payoutConfig.ownerBps;
            splits[1] = uint16((uint256(payoutConfig.partnerBps) * 10000) / remainingBps);
            splits[2] = uint16((uint256(payoutConfig.superAffiliateBps) * 10000) / remainingBps);
            splits[0] = 10000 - splits[1] - splits[2];
    
            if (tokenAddress == address(0)) {
              ArchetypePayouts(PAYOUTS).updateBalances{ value: remainingShare }(
                remainingShare,
                tokenAddress,
                recipients,
                splits
              );
            } else {
              ArchetypePayouts(PAYOUTS).updateBalances(
                remainingShare,
                tokenAddress,
                recipients,
                splits
              );
            }
          }
          emit Withdrawal(msgSender, tokenAddress, wad);
        }
      }
    
      function validateAffiliate(
        address affiliate,
        bytes calldata signature,
        address affiliateSigner
      ) public view {
        bytes32 signedMessagehash = ECDSA.toEthSignedMessageHash(
          keccak256(abi.encodePacked(affiliate))
        );
        address signer = ECDSA.recover(signedMessagehash, signature);
    
        if (signer != affiliateSigner) {
          revert InvalidSignature();
        }
      }
    
      function verify(
        Auth calldata auth,
        address tokenAddress,
        address account
      ) public pure returns (bool) {
        // keys 0-255 and tokenAddress are public
        if (uint256(auth.key) <= 0xff || auth.key == keccak256(abi.encodePacked(tokenAddress))) {
          return true;
        }
    
        return MerkleProofLib.verify(auth.proof, auth.key, keccak256(abi.encodePacked(account)));
      }
    
      function _msgSender() internal view returns (address) {
        return msg.sender == BATCH ? tx.origin : msg.sender;
      }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
    
    import 'erc721a-upgradeable/contracts/ERC721A__Initializable.sol';
    import 'erc721a-upgradeable/contracts/ERC721AUpgradeable.sol';
    
    pragma solidity ^0.8.4;
    
    /**
     * @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 ERC721A__OwnableUpgradeable is ERC721A__Initializable, ERC721AUpgradeable {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        function __Ownable_init() internal onlyInitializingERC721A {
            __Ownable_init_unchained();
        }
    
        function __Ownable_init_unchained() internal onlyInitializingERC721A {
            _transferOwnership(_msgSenderERC721A());
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _isOwner();
            _;
        }
    
        function _isOwner() internal view {
            require(owner() == _msgSenderERC721A(), "Ownable: caller is not the owner");
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing 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);
        }
    
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[49] private __gap;
    }

    // SPDX-License-Identifier: BUSL-1.1
    // Factory v0.8.0
    //
    // 8888888888                888
    // 888                       888
    // 888                       888
    // 8888888  8888b.   .d8888b 888888 .d88b.  888d888 888  888
    // 888         "88b d88P"    888   d88""88b 888P"   888  888
    // 888     .d888888 888      888   888  888 888     888  888
    // 888     888  888 Y88b.    Y88b. Y88..88P 888     Y88b 888
    // 888     "Y888888  "Y8888P  "Y888 "Y88P"  888      "Y88888
    //                                                       888
    //                                                  Y8b d88P
    //                                                   "Y88P"
    
    pragma solidity ^0.8.20;
    
    import "./ArchetypeErc721a.sol";
    import "./ArchetypeLogicErc721a.sol";
    import "@openzeppelin/contracts/proxy/Clones.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    
    contract FactoryErc721a is Ownable {
      event CollectionAdded(address indexed sender, address indexed receiver, address collection);
      address public archetype;
    
      constructor(address archetype_) {
        archetype = archetype_;
      }
    
      function createCollection(
        address _receiver,
        string memory name,
        string memory symbol,
        Config calldata config,
        PayoutConfig calldata payoutConfig
      ) external payable returns (address) {
        bytes32 salt = keccak256(abi.encodePacked(block.timestamp, msg.sender, block.chainid));
        address clone = Clones.cloneDeterministic(archetype, salt);
        ArchetypeErc721a token = ArchetypeErc721a(clone);
        token.initialize(name, symbol, config, payoutConfig, _receiver);
    
        token.transferOwnership(_receiver);
        if (msg.value > 0) {
          (bool sent, ) = payable(_receiver).call{ value: msg.value }("");
          require(sent, "1");
        }
        emit CollectionAdded(_msgSender(), _receiver, clone);
        return clone;
      }
    
      function setArchetype(address archetype_) public onlyOwner {
        archetype = archetype_;
      }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    /**
     * @dev This is a base contract to aid in writing upgradeable diamond facet contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     */
    
    import {ERC721A__InitializableStorage} from './ERC721A__InitializableStorage.sol';
    
    abstract contract ERC721A__Initializable {
        using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout;
    
        /**
         * @dev Modifier to protect an initializer function from being invoked twice.
         */
        modifier initializerERC721A() {
            // If the contract is initializing we ignore whether _initialized is set in order to support multiple
            // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
            // contract may have been reentered.
            require(
                ERC721A__InitializableStorage.layout()._initializing
                    ? _isConstructor()
                    : !ERC721A__InitializableStorage.layout()._initialized,
                'ERC721A__Initializable: contract is already initialized'
            );
    
            bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing;
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = true;
                ERC721A__InitializableStorage.layout()._initialized = true;
            }
    
            _;
    
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = false;
            }
        }
    
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} modifier, directly or indirectly.
         */
        modifier onlyInitializingERC721A() {
            require(
                ERC721A__InitializableStorage.layout()._initializing,
                'ERC721A__Initializable: contract is not initializing'
            );
            _;
        }
    
        /// @dev Returns true if and only if the function is running in the constructor
        function _isConstructor() private view returns (bool) {
            // extcodesize checks the size of the code stored in an address, and
            // address returns the current address. Since the code is still not
            // deployed when running a constructor, any checks on its code size will
            // yield zero, making it an effective way to detect if a contract is
            // under construction or not.
            address self = address(this);
            uint256 cs;
            assembly {
                cs := extcodesize(self)
            }
            return cs == 0;
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev This is a base storage for the  initialization function for upgradeable diamond facet contracts
     **/
    
    library ERC721A__InitializableStorage {
        struct Layout {
            /*
             * Indicates that the contract has been initialized.
             */
            bool _initialized;
            /*
             * Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
    
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.initializable.facet');
    
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    library ERC721AStorage {
        // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
        struct TokenApprovalRef {
            address value;
        }
    
        struct Layout {
            // =============================================================
            //                            STORAGE
            // =============================================================
    
            // The next token ID to be minted.
            uint256 _currentIndex;
            // The number of tokens burned.
            uint256 _burnCounter;
            // Token name
            string _name;
            // Token symbol
            string _symbol;
            // Mapping from token ID to ownership details
            // An empty struct value does not necessarily mean the token is unowned.
            // See {_packedOwnershipOf} implementation for details.
            //
            // Bits Layout:
            // - [0..159]   `addr`
            // - [160..223] `startTimestamp`
            // - [224]      `burned`
            // - [225]      `nextInitialized`
            // - [232..255] `extraData`
            mapping(uint256 => uint256) _packedOwnerships;
            // Mapping owner address to address data.
            //
            // Bits Layout:
            // - [0..63]    `balance`
            // - [64..127]  `numberMinted`
            // - [128..191] `numberBurned`
            // - [192..255] `aux`
            mapping(address => uint256) _packedAddressData;
            // Mapping from token ID to approved address.
            mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals;
            // Mapping from owner to operator approvals
            mapping(address => mapping(address => bool)) _operatorApprovals;
            // The amount of tokens minted above `_sequentialUpTo()`.
            // We call these spot mints (i.e. non-sequential mints).
            uint256 _spotMinted;
        }
    
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.ERC721A');
    
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    import './IERC721AUpgradeable.sol';
    import {ERC721AStorage} from './ERC721AStorage.sol';
    import './ERC721A__Initializable.sol';
    
    /**
     * @dev Interface of ERC721 token receiver.
     */
    interface ERC721A__IERC721ReceiverUpgradeable {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    
    /**
     * @title ERC721A
     *
     * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
     * Non-Fungible Token Standard, including the Metadata extension.
     * Optimized for lower gas during batch mints.
     *
     * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
     * starting from `_startTokenId()`.
     *
     * The `_sequentialUpTo()` function can be overriden to enable spot mints
     * (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
     *
     * Assumptions:
     *
     * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
     * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
     */
    contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable {
        using ERC721AStorage for ERC721AStorage.Layout;
    
        // =============================================================
        //                           CONSTANTS
        // =============================================================
    
        // Mask of an entry in packed address data.
        uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
    
        // The bit position of `numberMinted` in packed address data.
        uint256 private constant _BITPOS_NUMBER_MINTED = 64;
    
        // The bit position of `numberBurned` in packed address data.
        uint256 private constant _BITPOS_NUMBER_BURNED = 128;
    
        // The bit position of `aux` in packed address data.
        uint256 private constant _BITPOS_AUX = 192;
    
        // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
        uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
    
        // The bit position of `startTimestamp` in packed ownership.
        uint256 private constant _BITPOS_START_TIMESTAMP = 160;
    
        // The bit mask of the `burned` bit in packed ownership.
        uint256 private constant _BITMASK_BURNED = 1 << 224;
    
        // The bit position of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
    
        // The bit mask of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
    
        // The bit position of `extraData` in packed ownership.
        uint256 private constant _BITPOS_EXTRA_DATA = 232;
    
        // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
        uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
    
        // The mask of the lower 160 bits for addresses.
        uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
    
        // The maximum `quantity` that can be minted with {_mintERC2309}.
        // This limit is to prevent overflows on the address data entries.
        // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
        // is required to cause an overflow, which is unrealistic.
        uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
    
        // The `Transfer` event signature is given by:
        // `keccak256(bytes("Transfer(address,address,uint256)"))`.
        bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
            0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
    
        // =============================================================
        //                          CONSTRUCTOR
        // =============================================================
    
        function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            __ERC721A_init_unchained(name_, symbol_);
        }
    
        function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            ERC721AStorage.layout()._name = name_;
            ERC721AStorage.layout()._symbol = symbol_;
            ERC721AStorage.layout()._currentIndex = _startTokenId();
    
            if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
        }
    
        // =============================================================
        //                   TOKEN COUNTING OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the starting token ID for sequential mints.
         *
         * Override this function to change the starting token ID for sequential mints.
         *
         * Note: The value returned must never change after any tokens have been minted.
         */
        function _startTokenId() internal view virtual returns (uint256) {
            return 0;
        }
    
        /**
         * @dev Returns the maximum token ID (inclusive) for sequential mints.
         *
         * Override this function to return a value less than 2**256 - 1,
         * but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
         *
         * Note: The value returned must never change after any tokens have been minted.
         */
        function _sequentialUpTo() internal view virtual returns (uint256) {
            return type(uint256).max;
        }
    
        /**
         * @dev Returns the next token ID to be minted.
         */
        function _nextTokenId() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._currentIndex;
        }
    
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() public view virtual override returns (uint256 result) {
            // Counter underflow is impossible as `_burnCounter` cannot be incremented
            // more than `_currentIndex + _spotMinted - _startTokenId()` times.
            unchecked {
                // With spot minting, the intermediate `result` can be temporarily negative,
                // and the computation must be unchecked.
                result = ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
                if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
            }
        }
    
        /**
         * @dev Returns the total amount of tokens minted in the contract.
         */
        function _totalMinted() internal view virtual returns (uint256 result) {
            // Counter underflow is impossible as `_currentIndex` does not decrement,
            // and it is initialized to `_startTokenId()`.
            unchecked {
                result = ERC721AStorage.layout()._currentIndex - _startTokenId();
                if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
            }
        }
    
        /**
         * @dev Returns the total number of tokens burned.
         */
        function _totalBurned() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._burnCounter;
        }
    
        /**
         * @dev Returns the total number of tokens that are spot-minted.
         */
        function _totalSpotMinted() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._spotMinted;
        }
    
        // =============================================================
        //                    ADDRESS DATA OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
            return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the number of tokens minted by `owner`.
         */
        function _numberMinted(address owner) internal view returns (uint256) {
            return
                (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the number of tokens burned by or on behalf of `owner`.
         */
        function _numberBurned(address owner) internal view returns (uint256) {
            return
                (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         */
        function _getAux(address owner) internal view returns (uint64) {
            return uint64(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_AUX);
        }
    
        /**
         * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         * If there are multiple variables, please pack them into a uint64.
         */
        function _setAux(address owner, uint64 aux) internal virtual {
            uint256 packed = ERC721AStorage.layout()._packedAddressData[owner];
            uint256 auxCasted;
            // Cast `aux` with assembly to avoid redundant masking.
            assembly {
                auxCasted := aux
            }
            packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
            ERC721AStorage.layout()._packedAddressData[owner] = packed;
        }
    
        // =============================================================
        //                            IERC165
        // =============================================================
    
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            // The interface IDs are constants representing the first 4 bytes
            // of the XOR of all function selectors in the interface.
            // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
            // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
            return
                interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
        }
    
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
    
        /**
         * @dev Returns the token collection name.
         */
        function name() public view virtual override returns (string memory) {
            return ERC721AStorage.layout()._name;
        }
    
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return ERC721AStorage.layout()._symbol;
        }
    
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
    
            string memory baseURI = _baseURI();
            return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
        }
    
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, it can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return '';
        }
    
        // =============================================================
        //                     OWNERSHIPS OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return address(uint160(_packedOwnershipOf(tokenId)));
        }
    
        /**
         * @dev Gas spent here starts off proportional to the maximum mint batch size.
         * It gradually moves to O(1) as tokens get transferred around over time.
         */
        function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnershipOf(tokenId));
        }
    
        /**
         * @dev Returns the unpacked `TokenOwnership` struct at `index`.
         */
        function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]);
        }
    
        /**
         * @dev Returns whether the ownership slot at `index` is initialized.
         * An uninitialized slot does not necessarily mean that the slot has no owner.
         */
        function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
            return ERC721AStorage.layout()._packedOwnerships[index] != 0;
        }
    
        /**
         * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
         */
        function _initializeOwnershipAt(uint256 index) internal virtual {
            if (ERC721AStorage.layout()._packedOwnerships[index] == 0) {
                ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index);
            }
        }
    
        /**
         * @dev Returns the packed ownership data of `tokenId`.
         */
        function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
            if (_startTokenId() <= tokenId) {
                packed = ERC721AStorage.layout()._packedOwnerships[tokenId];
    
                if (tokenId > _sequentialUpTo()) {
                    if (_packedOwnershipExists(packed)) return packed;
                    _revert(OwnerQueryForNonexistentToken.selector);
                }
    
                // If the data at the starting slot does not exist, start the scan.
                if (packed == 0) {
                    if (tokenId >= ERC721AStorage.layout()._currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
                    // Invariant:
                    // There will always be an initialized ownership slot
                    // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                    // before an unintialized ownership slot
                    // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                    // Hence, `tokenId` will not underflow.
                    //
                    // We can directly compare the packed value.
                    // If the address is zero, packed will be zero.
                    for (;;) {
                        unchecked {
                            packed = ERC721AStorage.layout()._packedOwnerships[--tokenId];
                        }
                        if (packed == 0) continue;
                        if (packed & _BITMASK_BURNED == 0) return packed;
                        // Otherwise, the token is burned, and we must revert.
                        // This handles the case of batch burned tokens, where only the burned bit
                        // of the starting slot is set, and remaining slots are left uninitialized.
                        _revert(OwnerQueryForNonexistentToken.selector);
                    }
                }
                // Otherwise, the data exists and we can skip the scan.
                // This is possible because we have already achieved the target condition.
                // This saves 2143 gas on transfers of initialized tokens.
                // If the token is not burned, return `packed`. Otherwise, revert.
                if (packed & _BITMASK_BURNED == 0) return packed;
            }
            _revert(OwnerQueryForNonexistentToken.selector);
        }
    
        /**
         * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
         */
        function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
            ownership.addr = address(uint160(packed));
            ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
            ownership.burned = packed & _BITMASK_BURNED != 0;
            ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
        }
    
        /**
         * @dev Packs ownership data into a single uint256.
         */
        function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
            }
        }
    
        /**
         * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
         */
        function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
            // For branchless setting of the `nextInitialized` flag.
            assembly {
                // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
            }
        }
    
        // =============================================================
        //                      APPROVAL OPERATIONS
        // =============================================================
    
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         */
        function approve(address to, uint256 tokenId) public payable virtual override {
            _approve(to, tokenId, true);
        }
    
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
    
            return ERC721AStorage.layout()._tokenApprovals[tokenId].value;
        }
    
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom}
         * for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            ERC721AStorage.layout()._operatorApprovals[_msgSenderERC721A()][operator] = approved;
            emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
        }
    
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return ERC721AStorage.layout()._operatorApprovals[owner][operator];
        }
    
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted. See {_mint}.
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool result) {
            if (_startTokenId() <= tokenId) {
                if (tokenId > _sequentialUpTo())
                    return _packedOwnershipExists(ERC721AStorage.layout()._packedOwnerships[tokenId]);
    
                if (tokenId < ERC721AStorage.layout()._currentIndex) {
                    uint256 packed;
                    while ((packed = ERC721AStorage.layout()._packedOwnerships[tokenId]) == 0) --tokenId;
                    result = packed & _BITMASK_BURNED == 0;
                }
            }
        }
    
        /**
         * @dev Returns whether `packed` represents a token that exists.
         */
        function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
            assembly {
                // The following is equivalent to `owner != address(0) && burned == false`.
                // Symbolically tested.
                result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
            }
        }
    
        /**
         * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
         */
        function _isSenderApprovedOrOwner(
            address approvedAddress,
            address owner,
            address msgSender
        ) private pure returns (bool result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                msgSender := and(msgSender, _BITMASK_ADDRESS)
                // `msgSender == owner || msgSender == approvedAddress`.
                result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
            }
        }
    
        /**
         * @dev Returns the storage slot and value for the approved address of `tokenId`.
         */
        function _getApprovedSlotAndAddress(uint256 tokenId)
            private
            view
            returns (uint256 approvedAddressSlot, address approvedAddress)
        {
            ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._tokenApprovals[tokenId];
            // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
            assembly {
                approvedAddressSlot := tokenApproval.slot
                approvedAddress := sload(approvedAddressSlot)
            }
        }
    
        // =============================================================
        //                      TRANSFER OPERATIONS
        // =============================================================
    
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token
         * by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public payable virtual override {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
    
            // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
            from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
    
            if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
    
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
    
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
    
            _beforeTokenTransfers(from, to, tokenId, 1);
    
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
    
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // We can directly increment and decrement the balances.
                --ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`.
                ++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`.
    
                // Updates:
                // - `address` to the next owner.
                // - `startTimestamp` to the timestamp of transfering.
                // - `burned` to `false`.
                // - `nextInitialized` to `true`.
                ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                    to,
                    _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                );
    
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
    
            // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
            uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
            assembly {
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    from, // `from`.
                    toMasked, // `to`.
                    tokenId // `tokenId`.
                )
            }
            if (toMasked == 0) _revert(TransferToZeroAddress.selector);
    
            _afterTokenTransfers(from, to, tokenId, 1);
        }
    
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public payable virtual override {
            safeTransferFrom(from, to, tokenId, '');
        }
    
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token
         * by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) public payable virtual override {
            transferFrom(from, to, tokenId);
            if (to.code.length != 0)
                if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                    _revert(TransferToNonERC721ReceiverImplementer.selector);
                }
        }
    
        /**
         * @dev Hook that is called before a set of serially-ordered token IDs
         * are about to be transferred. This includes minting.
         * And also called before burning one token.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _beforeTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
    
        /**
         * @dev Hook that is called after a set of serially-ordered token IDs
         * have been transferred. This includes minting.
         * And also called after one token has been burned.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
         * transferred to `to`.
         * - When `from` is zero, `tokenId` has been minted for `to`.
         * - When `to` is zero, `tokenId` has been burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
    
        /**
         * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
         *
         * `from` - Previous owner of the given token ID.
         * `to` - Target address that will receive the token.
         * `tokenId` - Token ID to be transferred.
         * `_data` - Optional data to send along with the call.
         *
         * Returns whether the call correctly returned the expected magic value.
         */
        function _checkContractOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            try
                ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data)
            returns (bytes4 retval) {
                return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    _revert(TransferToNonERC721ReceiverImplementer.selector);
                }
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    
        // =============================================================
        //                        MINT OPERATIONS
        // =============================================================
    
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mint(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
            if (quantity == 0) _revert(MintZeroQuantity.selector);
    
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
    
            // Overflows are incredibly unrealistic.
            // `balance` and `numberMinted` have a maximum limit of 2**64.
            // `tokenId` has a maximum limit of 2**256.
            unchecked {
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
    
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
    
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
    
                if (toMasked == 0) _revert(MintToZeroAddress.selector);
    
                uint256 end = startTokenId + quantity;
                uint256 tokenId = startTokenId;
    
                if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
    
                do {
                    assembly {
                        // Emit the `Transfer` event.
                        log4(
                            0, // Start of data (0, since no data).
                            0, // End of data (0, since no data).
                            _TRANSFER_EVENT_SIGNATURE, // Signature.
                            0, // `address(0)`.
                            toMasked, // `to`.
                            tokenId // `tokenId`.
                        )
                    }
                    // The `!=` check ensures that large values of `quantity`
                    // that overflows uint256 will make the loop run out of gas.
                } while (++tokenId != end);
    
                ERC721AStorage.layout()._currentIndex = end;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
    
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * This function is intended for efficient minting only during contract creation.
         *
         * It emits only one {ConsecutiveTransfer} as defined in
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
         * instead of a sequence of {Transfer} event(s).
         *
         * Calling this function outside of contract creation WILL make your contract
         * non-compliant with the ERC721 standard.
         * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
         * {ConsecutiveTransfer} event is only permissible during contract creation.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {ConsecutiveTransfer} event.
         */
        function _mintERC2309(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
            if (to == address(0)) _revert(MintToZeroAddress.selector);
            if (quantity == 0) _revert(MintZeroQuantity.selector);
            if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
    
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
    
            // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
    
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
    
                if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
    
                emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
    
                ERC721AStorage.layout()._currentIndex = startTokenId + quantity;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
    
        /**
         * @dev Safely mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
         * - `quantity` must be greater than 0.
         *
         * See {_mint}.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _safeMint(
            address to,
            uint256 quantity,
            bytes memory _data
        ) internal virtual {
            _mint(to, quantity);
    
            unchecked {
                if (to.code.length != 0) {
                    uint256 end = ERC721AStorage.layout()._currentIndex;
                    uint256 index = end - quantity;
                    do {
                        if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                            _revert(TransferToNonERC721ReceiverImplementer.selector);
                        }
                    } while (index < end);
                    // This prevents reentrancy to `_safeMint`.
                    // It does not prevent reentrancy to `_safeMintSpot`.
                    if (ERC721AStorage.layout()._currentIndex != end) revert();
                }
            }
        }
    
        /**
         * @dev Equivalent to `_safeMint(to, quantity, '')`.
         */
        function _safeMint(address to, uint256 quantity) internal virtual {
            _safeMint(to, quantity, '');
        }
    
        /**
         * @dev Mints a single token at `tokenId`.
         *
         * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` must be greater than `_sequentialUpTo()`.
         * - `tokenId` must not exist.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mintSpot(address to, uint256 tokenId) internal virtual {
            if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
            uint256 prevOwnershipPacked = ERC721AStorage.layout()._packedOwnerships[tokenId];
            if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
    
            _beforeTokenTransfers(address(0), to, tokenId, 1);
    
            // Overflows are incredibly unrealistic.
            // The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
            // `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
            unchecked {
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `true` (as `quantity == 1`).
                ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
                );
    
                // Updates:
                // - `balance += 1`.
                // - `numberMinted += 1`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
    
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
    
                if (toMasked == 0) _revert(MintToZeroAddress.selector);
    
                assembly {
                    // Emit the `Transfer` event.
                    log4(
                        0, // Start of data (0, since no data).
                        0, // End of data (0, since no data).
                        _TRANSFER_EVENT_SIGNATURE, // Signature.
                        0, // `address(0)`.
                        toMasked, // `to`.
                        tokenId // `tokenId`.
                    )
                }
    
                ++ERC721AStorage.layout()._spotMinted;
            }
    
            _afterTokenTransfers(address(0), to, tokenId, 1);
        }
    
        /**
         * @dev Safely mints a single token at `tokenId`.
         *
         * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
         * - `tokenId` must be greater than `_sequentialUpTo()`.
         * - `tokenId` must not exist.
         *
         * See {_mintSpot}.
         *
         * Emits a {Transfer} event.
         */
        function _safeMintSpot(
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _mintSpot(to, tokenId);
    
            unchecked {
                if (to.code.length != 0) {
                    uint256 currentSpotMinted = ERC721AStorage.layout()._spotMinted;
                    if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
                        _revert(TransferToNonERC721ReceiverImplementer.selector);
                    }
                    // This prevents reentrancy to `_safeMintSpot`.
                    // It does not prevent reentrancy to `_safeMint`.
                    if (ERC721AStorage.layout()._spotMinted != currentSpotMinted) revert();
                }
            }
        }
    
        /**
         * @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
         */
        function _safeMintSpot(address to, uint256 tokenId) internal virtual {
            _safeMintSpot(to, tokenId, '');
        }
    
        // =============================================================
        //                       APPROVAL OPERATIONS
        // =============================================================
    
        /**
         * @dev Equivalent to `_approve(to, tokenId, false)`.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _approve(to, tokenId, false);
        }
    
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function _approve(
            address to,
            uint256 tokenId,
            bool approvalCheck
        ) internal virtual {
            address owner = ownerOf(tokenId);
    
            if (approvalCheck && _msgSenderERC721A() != owner)
                if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                    _revert(ApprovalCallerNotOwnerNorApproved.selector);
                }
    
            ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
            emit Approval(owner, to, tokenId);
        }
    
        // =============================================================
        //                        BURN OPERATIONS
        // =============================================================
    
        /**
         * @dev Equivalent to `_burn(tokenId, false)`.
         */
        function _burn(uint256 tokenId) internal virtual {
            _burn(tokenId, false);
        }
    
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
    
            address from = address(uint160(prevOwnershipPacked));
    
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
    
            if (approvalCheck) {
                // The nested ifs save around 20+ gas over a compound boolean condition.
                if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                    if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
            }
    
            _beforeTokenTransfers(from, address(0), tokenId, 1);
    
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
    
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // Updates:
                // - `balance -= 1`.
                // - `numberBurned += 1`.
                //
                // We can directly decrement the balance, and increment the number burned.
                // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                ERC721AStorage.layout()._packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
    
                // Updates:
                // - `address` to the last owner.
                // - `startTimestamp` to the timestamp of burning.
                // - `burned` to `true`.
                // - `nextInitialized` to `true`.
                ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                    from,
                    (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                );
    
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
    
            emit Transfer(from, address(0), tokenId);
            _afterTokenTransfers(from, address(0), tokenId, 1);
    
            // Overflow not possible, as `_burnCounter` cannot be exceed `_currentIndex + _spotMinted` times.
            unchecked {
                ERC721AStorage.layout()._burnCounter++;
            }
        }
    
        // =============================================================
        //                     EXTRA DATA OPERATIONS
        // =============================================================
    
        /**
         * @dev Directly sets the extra data for the ownership data `index`.
         */
        function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
            uint256 packed = ERC721AStorage.layout()._packedOwnerships[index];
            if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
            uint256 extraDataCasted;
            // Cast `extraData` with assembly to avoid redundant masking.
            assembly {
                extraDataCasted := extraData
            }
            packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
            ERC721AStorage.layout()._packedOwnerships[index] = packed;
        }
    
        /**
         * @dev Called during each token transfer to set the 24bit `extraData` field.
         * Intended to be overridden by the cosumer contract.
         *
         * `previousExtraData` - the value of `extraData` before transfer.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _extraData(
            address from,
            address to,
            uint24 previousExtraData
        ) internal view virtual returns (uint24) {}
    
        /**
         * @dev Returns the next extra data for the packed ownership data.
         * The returned result is shifted into position.
         */
        function _nextExtraData(
            address from,
            address to,
            uint256 prevOwnershipPacked
        ) private view returns (uint256) {
            uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
            return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
        }
    
        // =============================================================
        //                       OTHER OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the message sender (defaults to `msg.sender`).
         *
         * If you are writing GSN compatible contracts, you need to override this function.
         */
        function _msgSenderERC721A() internal view virtual returns (address) {
            return msg.sender;
        }
    
        /**
         * @dev Converts a uint256 to its ASCII string decimal representation.
         */
        function _toString(uint256 value) internal pure virtual returns (string memory str) {
            assembly {
                // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                // We will need 1 word for the trailing zeros padding, 1 word for the length,
                // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
                let m := add(mload(0x40), 0xa0)
                // Update the free memory pointer to allocate.
                mstore(0x40, m)
                // Assign the `str` to the end.
                str := sub(m, 0x20)
                // Zeroize the slot after the string.
                mstore(str, 0)
    
                // Cache the end of the memory to calculate the length later.
                let end := str
    
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                // prettier-ignore
                for { let temp := value } 1 {} {
                    str := sub(str, 1)
                    // Write the character to the pointer.
                    // The ASCII index of the '0' character is 48.
                    mstore8(str, add(48, mod(temp, 10)))
                    // Keep dividing `temp` until zero.
                    temp := div(temp, 10)
                    // prettier-ignore
                    if iszero(temp) { break }
                }
    
                let length := sub(end, str)
                // Move the pointer 32 bytes leftwards to make room for the length.
                str := sub(str, 0x20)
                // Store the length.
                mstore(str, length)
            }
        }
    
        /**
         * @dev For more efficient reverts.
         */
        function _revert(bytes4 errorSelector) internal pure {
            assembly {
                mstore(0x00, errorSelector)
                revert(0x00, 0x04)
            }
        }
    }

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    import './IERC721AQueryableUpgradeable.sol';
    import '../ERC721AUpgradeable.sol';
    import '../ERC721A__Initializable.sol';
    
    /**
     * @title ERC721AQueryable.
     *
     * @dev ERC721A subclass with convenience query functions.
     */
    abstract contract ERC721AQueryableUpgradeable is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        IERC721AQueryableUpgradeable
    {
        function __ERC721AQueryable_init() internal onlyInitializingERC721A {
            __ERC721AQueryable_init_unchained();
        }
    
        function __ERC721AQueryable_init_unchained() internal onlyInitializingERC721A {}
    
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId)
            public
            view
            virtual
            override
            returns (TokenOwnership memory ownership)
        {
            unchecked {
                if (tokenId >= _startTokenId()) {
                    if (tokenId > _sequentialUpTo()) return _ownershipAt(tokenId);
    
                    if (tokenId < _nextTokenId()) {
                        // If the `tokenId` is within bounds,
                        // scan backwards for the initialized ownership slot.
                        while (!_ownershipIsInitialized(tokenId)) --tokenId;
                        return _ownershipAt(tokenId);
                    }
                }
            }
        }
    
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] calldata tokenIds)
            external
            view
            virtual
            override
            returns (TokenOwnership[] memory)
        {
            TokenOwnership[] memory ownerships;
            uint256 i = tokenIds.length;
            assembly {
                // Grab the free memory pointer.
                ownerships := mload(0x40)
                // Store the length.
                mstore(ownerships, i)
                // Allocate one word for the length,
                // `tokenIds.length` words for the pointers.
                i := shl(5, i) // Multiply `i` by 32.
                mstore(0x40, add(add(ownerships, 0x20), i))
            }
            while (i != 0) {
                uint256 tokenId;
                assembly {
                    i := sub(i, 0x20)
                    tokenId := calldataload(add(tokenIds.offset, i))
                }
                TokenOwnership memory ownership = explicitOwnershipOf(tokenId);
                assembly {
                    // Store the pointer of `ownership` in the `ownerships` array.
                    mstore(add(add(ownerships, 0x20), i), ownership)
                }
            }
            return ownerships;
        }
    
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view virtual override returns (uint256[] memory) {
            return _tokensOfOwnerIn(owner, start, stop);
        }
    
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
            // If spot mints are enabled, full-range scan is disabled.
            if (_sequentialUpTo() != type(uint256).max) _revert(NotCompatibleWithSpotMints.selector);
            uint256 start = _startTokenId();
            uint256 stop = _nextTokenId();
            uint256[] memory tokenIds;
            if (start != stop) tokenIds = _tokensOfOwnerIn(owner, start, stop);
            return tokenIds;
        }
    
        /**
         * @dev Helper function for returning an array of token IDs owned by `owner`.
         *
         * Note that this function is optimized for smaller bytecode size over runtime gas,
         * since it is meant to be called off-chain.
         */
        function _tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) private view returns (uint256[] memory tokenIds) {
            unchecked {
                if (start >= stop) _revert(InvalidQueryRange.selector);
                // Set `start = max(start, _startTokenId())`.
                if (start < _startTokenId()) start = _startTokenId();
                uint256 nextTokenId = _nextTokenId();
                // If spot mints are enabled, scan all the way until the specified `stop`.
                uint256 stopLimit = _sequentialUpTo() != type(uint256).max ? stop : nextTokenId;
                // Set `stop = min(stop, stopLimit)`.
                if (stop >= stopLimit) stop = stopLimit;
                // Number of tokens to scan.
                uint256 tokenIdsMaxLength = balanceOf(owner);
                // Set `tokenIdsMaxLength` to zero if the range contains no tokens.
                if (start >= stop) tokenIdsMaxLength = 0;
                // If there are one or more tokens to scan.
                if (tokenIdsMaxLength != 0) {
                    // Set `tokenIdsMaxLength = min(balanceOf(owner), tokenIdsMaxLength)`.
                    if (stop - start <= tokenIdsMaxLength) tokenIdsMaxLength = stop - start;
                    uint256 m; // Start of available memory.
                    assembly {
                        // Grab the free memory pointer.
                        tokenIds := mload(0x40)
                        // Allocate one word for the length, and `tokenIdsMaxLength` words
                        // for the data. `shl(5, x)` is equivalent to `mul(32, x)`.
                        m := add(tokenIds, shl(5, add(tokenIdsMaxLength, 1)))
                        mstore(0x40, m)
                    }
                    // We need to call `explicitOwnershipOf(start)`,
                    // because the slot at `start` may not be initialized.
                    TokenOwnership memory ownership = explicitOwnershipOf(start);
                    address currOwnershipAddr;
                    // If the starting slot exists (i.e. not burned),
                    // initialize `currOwnershipAddr`.
                    // `ownership.address` will not be zero,
                    // as `start` is clamped to the valid token ID range.
                    if (!ownership.burned) currOwnershipAddr = ownership.addr;
                    uint256 tokenIdsIdx;
                    // Use a do-while, which is slightly more efficient for this case,
                    // as the array will at least contain one element.
                    do {
                        if (_sequentialUpTo() != type(uint256).max) {
                            // Skip the remaining unused sequential slots.
                            if (start == nextTokenId) start = _sequentialUpTo() + 1;
                            // Reset `currOwnershipAddr`, as each spot-minted token is a batch of one.
                            if (start > _sequentialUpTo()) currOwnershipAddr = address(0);
                        }
                        ownership = _ownershipAt(start); // This implicitly allocates memory.
                        assembly {
                            switch mload(add(ownership, 0x40))
                            // if `ownership.burned == false`.
                            case 0 {
                                // if `ownership.addr != address(0)`.
                                // The `addr` already has it's upper 96 bits clearned,
                                // since it is written to memory with regular Solidity.
                                if mload(ownership) {
                                    currOwnershipAddr := mload(ownership)
                                }
                                // if `currOwnershipAddr == owner`.
                                // The `shl(96, x)` is to make the comparison agnostic to any
                                // dirty upper 96 bits in `owner`.
                                if iszero(shl(96, xor(currOwnershipAddr, owner))) {
                                    tokenIdsIdx := add(tokenIdsIdx, 1)
                                    mstore(add(tokenIds, shl(5, tokenIdsIdx)), start)
                                }
                            }
                            // Otherwise, reset `currOwnershipAddr`.
                            // This handles the case of batch burned tokens
                            // (burned bit of first slot set, remaining slots left uninitialized).
                            default {
                                currOwnershipAddr := 0
                            }
                            start := add(start, 1)
                            // Free temporary memory implicitly allocated for ownership
                            // to avoid quadratic memory expansion costs.
                            mstore(0x40, m)
                        }
                    } while (!(start == stop || tokenIdsIdx == tokenIdsMaxLength));
                    // Store the length of the array.
                    assembly {
                        mstore(tokenIds, tokenIdsIdx)
                    }
                }
            }
        }
    }

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    import '../IERC721AUpgradeable.sol';
    
    /**
     * @dev Interface of ERC721AQueryable.
     */
    interface IERC721AQueryableUpgradeable is IERC721AUpgradeable {
        /**
         * Invalid query range (`start` >= `stop`).
         */
        error InvalidQueryRange();
    
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
    
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
    
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view returns (uint256[] memory);
    
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view returns (uint256[] memory);
    }

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    /**
     * @dev Interface of ERC721A.
     */
    interface IERC721AUpgradeable {
        /**
         * The caller must own the token or be an approved operator.
         */
        error ApprovalCallerNotOwnerNorApproved();
    
        /**
         * The token does not exist.
         */
        error ApprovalQueryForNonexistentToken();
    
        /**
         * Cannot query the balance for the zero address.
         */
        error BalanceQueryForZeroAddress();
    
        /**
         * Cannot mint to the zero address.
         */
        error MintToZeroAddress();
    
        /**
         * The quantity of tokens minted must be more than zero.
         */
        error MintZeroQuantity();
    
        /**
         * The token does not exist.
         */
        error OwnerQueryForNonexistentToken();
    
        /**
         * The caller must own the token or be an approved operator.
         */
        error TransferCallerNotOwnerNorApproved();
    
        /**
         * The token must be owned by `from`.
         */
        error TransferFromIncorrectOwner();
    
        /**
         * Cannot safely transfer to a contract that does not implement the
         * ERC721Receiver interface.
         */
        error TransferToNonERC721ReceiverImplementer();
    
        /**
         * Cannot transfer to the zero address.
         */
        error TransferToZeroAddress();
    
        /**
         * The token does not exist.
         */
        error URIQueryForNonexistentToken();
    
        /**
         * The `quantity` minted with ERC2309 exceeds the safety limit.
         */
        error MintERC2309QuantityExceedsLimit();
    
        /**
         * The `extraData` cannot be set on an unintialized ownership slot.
         */
        error OwnershipNotInitializedForExtraData();
    
        /**
         * `_sequentialUpTo()` must be greater than `_startTokenId()`.
         */
        error SequentialUpToTooSmall();
    
        /**
         * The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
         */
        error SequentialMintExceedsLimit();
    
        /**
         * Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
         */
        error SpotMintTokenIdTooSmall();
    
        /**
         * Cannot mint over a token that already exists.
         */
        error TokenAlreadyExists();
    
        /**
         * The feature is not compatible with spot mints.
         */
        error NotCompatibleWithSpotMints();
    
        // =============================================================
        //                            STRUCTS
        // =============================================================
    
        struct TokenOwnership {
            // The address of the owner.
            address addr;
            // Stores the start time of ownership with minimal overhead for tokenomics.
            uint64 startTimestamp;
            // Whether the token has been burned.
            bool burned;
            // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
            uint24 extraData;
        }
    
        // =============================================================
        //                         TOKEN COUNTERS
        // =============================================================
    
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() external view returns (uint256);
    
        // =============================================================
        //                            IERC165
        // =============================================================
    
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    
        // =============================================================
        //                            IERC721
        // =============================================================
    
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    
        /**
         * @dev Emitted when `owner` enables or disables
         * (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
    
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
    
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`,
         * checking first that contract recipients are aware of the ERC721 protocol
         * to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move
         * this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external payable;
    
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external payable;
    
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
         * whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token
         * by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external payable;
    
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external payable;
    
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom}
         * for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
    
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
    
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
    
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
    
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
    
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
    
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    
        // =============================================================
        //                           IERC2309
        // =============================================================
    
        /**
         * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
         * (inclusive) is transferred from `from` to `to`, as defined in the
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
         *
         * See {_mintERC2309} for more details.
         */
        event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Gas optimized ECDSA wrapper.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
    ///
    /// @dev Note:
    /// - The recovery functions use the ecrecover precompile (0x1).
    /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
    ///   This is for more safety by default.
    ///   Use the `tryRecover` variants if you need to get the zero address back
    ///   upon recovery failure instead.
    /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
    ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
    ///   See: https://eips.ethereum.org/EIPS/eip-2098
    ///   This is for calldata efficiency on smart accounts prevalent on L2s.
    ///
    /// WARNING! Do NOT directly use signatures as unique identifiers:
    /// - The recovery operations do NOT check if a signature is non-malleable.
    /// - Use a nonce in the digest to prevent replay attacks on the same contract.
    /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
    ///   EIP-712 also enables readable signing of typed data for better user safety.
    /// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
    library ECDSA {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The order of the secp256k1 elliptic curve.
        uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
    
        /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
        uint256 private constant _HALF_N_PLUS_1 =
            0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CUSTOM ERRORS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The signature is invalid.
        error InvalidSignature();
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    RECOVERY OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                for {} 1 {} {
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    if eq(mload(signature), 64) {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(mload(signature), 65) {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                        break
                    }
                    result := 0
                    break
                }
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            result, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                for {} 1 {} {
                    if eq(signature.length, 64) {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(signature.length, 65) {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                        break
                    }
                    result := 0
                    break
                }
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            result, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   TRY-RECOVER OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // WARNING!
        // These functions will NOT revert upon recovery failure.
        // Instead, they will return the zero address upon recovery failure.
        // It is critical that the returned address is NEVER compared against
        // a zero address (e.g. an uninitialized address variable).
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecover(bytes32 hash, bytes memory signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                for {} 1 {} {
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    if eq(mload(signature), 64) {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(mload(signature), 65) {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                        break
                    }
                    result := 0
                    break
                }
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                for {} 1 {} {
                    if eq(signature.length, 64) {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(signature.length, 65) {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                        break
                    }
                    result := 0
                    break
                }
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HASHING OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an Ethereum Signed Message, created from a `hash`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, hash) // Store into scratch space for keccak256.
                mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
                result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
            }
        }
    
        /// @dev Returns an Ethereum Signed Message, created from `s`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        /// Note: Supports lengths of `s` up to 999999 bytes.
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let sLength := mload(s)
                let o := 0x20
                mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
                mstore(0x00, 0x00)
                // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                for { let temp := sLength } 1 {} {
                    o := sub(o, 1)
                    mstore8(o, add(48, mod(temp, 10)))
                    temp := div(temp, 10)
                    if iszero(temp) { break }
                }
                let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                mstore(s, sLength) // Restore the length.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  CANONICAL HASH FUNCTIONS                  */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // The following functions returns the hash of the signature in it's canonicalized format,
        // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
        // If `s` is greater than `N / 2` then it will be converted to `N - s`
        // and the `v` value will be flipped.
        // If the signature has an invalid length, or if `v` is invalid,
        // a uniquely corrupt hash will be returned.
        // These functions are useful for "poor-mans-VRF".
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
            // @solidity memory-safe-assembly
            assembly {
                let l := mload(signature)
                for {} 1 {} {
                    mstore(0x00, mload(add(signature, 0x20))) // `r`.
                    let s := mload(add(signature, 0x40))
                    let v := mload(add(signature, 0x41))
                    if eq(l, 64) {
                        v := add(shr(255, s), 27)
                        s := shr(1, shl(1, s))
                    }
                    if iszero(lt(s, _HALF_N_PLUS_1)) {
                        v := xor(v, 7)
                        s := sub(N, s)
                    }
                    mstore(0x21, v)
                    mstore(0x20, s)
                    result := keccak256(0x00, 0x41)
                    mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    break
                }
    
                // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                if iszero(lt(sub(l, 64), 2)) {
                    // `bytes4(keccak256("InvalidSignatureLength"))`.
                    result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
                }
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHashCalldata(bytes calldata signature)
            internal
            pure
            returns (bytes32 result)
        {
            // @solidity memory-safe-assembly
            assembly {
                let l := signature.length
                for {} 1 {} {
                    mstore(0x00, calldataload(signature.offset)) // `r`.
                    let s := calldataload(add(signature.offset, 0x20))
                    let v := calldataload(add(signature.offset, 0x21))
                    if eq(l, 64) {
                        v := add(shr(255, s), 27)
                        s := shr(1, shl(1, s))
                    }
                    if iszero(lt(s, _HALF_N_PLUS_1)) {
                        v := xor(v, 7)
                        s := sub(N, s)
                    }
                    mstore(0x21, v)
                    mstore(0x20, s)
                    result := keccak256(0x00, 0x41)
                    mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    break
                }
                // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                if iszero(lt(sub(l, 64), 2)) {
                    calldatacopy(mload(0x40), signature.offset, l)
                    // `bytes4(keccak256("InvalidSignatureLength"))`.
                    result := xor(keccak256(mload(0x40), l), 0xd62f1ab2)
                }
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
            // @solidity memory-safe-assembly
            assembly {
                mstore(0x00, r) // `r`.
                let v := add(shr(255, vs), 27)
                let s := shr(1, shl(1, vs))
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
            // @solidity memory-safe-assembly
            assembly {
                mstore(0x00, r) // `r`.
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an empty calldata bytes.
        function emptySignature() internal pure returns (bytes calldata signature) {
            /// @solidity memory-safe-assembly
            assembly {
                signature.length := 0
            }
        }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Library for converting numbers into strings and other string operations.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
    ///
    /// @dev Note:
    /// For performance and bytecode compactness, most of the string operations are restricted to
    /// byte strings (7-bit ASCII), except where otherwise specified.
    /// Usage of byte string operations on charsets with runes spanning two or more bytes
    /// can lead to undefined behavior.
    library LibString {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CUSTOM ERRORS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The length of the output is too small to contain all the hex digits.
        error HexLengthInsufficient();
    
        /// @dev The length of the string is more than 32 bytes.
        error TooBigForSmallString();
    
        /// @dev The input string must be a 7-bit ASCII.
        error StringNot7BitASCII();
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The constant returned when the `search` is not found in the string.
        uint256 internal constant NOT_FOUND = type(uint256).max;
    
        /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
        uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
    
        /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
        uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
    
        /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
        uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
    
        /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
        uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
    
        /// @dev Lookup for '0123456789'.
        uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
    
        /// @dev Lookup for '0123456789abcdefABCDEF'.
        uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
    
        /// @dev Lookup for '01234567'.
        uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
    
        /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
        uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
    
        /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
        uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
    
        /// @dev Lookup for ' \t\n\r\x0b\x0c'.
        uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     DECIMAL OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the base 10 decimal representation of `value`.
        function toString(uint256 value) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                // We will need 1 word for the trailing zeros padding, 1 word for the length,
                // and 3 words for a maximum of 78 digits.
                result := add(mload(0x40), 0x80)
                mstore(0x40, add(result, 0x20)) // Allocate memory.
                mstore(result, 0) // Zeroize the slot after the string.
    
                let end := result // Cache the end of the memory to calculate the length later.
                let w := not(0) // Tsk.
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                for { let temp := value } 1 {} {
                    result := add(result, w) // `sub(result, 1)`.
                    // Store the character to the pointer.
                    // The ASCII index of the '0' character is 48.
                    mstore8(result, add(48, mod(temp, 10)))
                    temp := div(temp, 10) // Keep dividing `temp` until zero.
                    if iszero(temp) { break }
                }
                let n := sub(end, result)
                result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the base 10 decimal representation of `value`.
        function toString(int256 value) internal pure returns (string memory result) {
            if (value >= 0) return toString(uint256(value));
            unchecked {
                result = toString(~uint256(value) + 1);
            }
            /// @solidity memory-safe-assembly
            assembly {
                // We still have some spare memory space on the left,
                // as we have allocated 3 words (96 bytes) for up to 78 digits.
                let n := mload(result) // Load the string length.
                mstore(result, 0x2d) // Store the '-' character.
                result := sub(result, 1) // Move back the string pointer by a byte.
                mstore(result, add(n, 1)) // Update the string length.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   HEXADECIMAL OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the hexadecimal representation of `value`,
        /// left-padded to an input length of `length` bytes.
        /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
        /// giving a total length of `length * 2 + 2` bytes.
        /// Reverts if `length` is too small for the output to contain all the digits.
        function toHexString(uint256 value, uint256 length)
            internal
            pure
            returns (string memory result)
        {
            result = toHexStringNoPrefix(value, length);
            /// @solidity memory-safe-assembly
            assembly {
                let n := add(mload(result), 2) // Compute the length.
                mstore(result, 0x3078) // Store the "0x" prefix.
                result := sub(result, 2) // Move the pointer.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`,
        /// left-padded to an input length of `length` bytes.
        /// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
        /// giving a total length of `length * 2` bytes.
        /// Reverts if `length` is too small for the output to contain all the digits.
        function toHexStringNoPrefix(uint256 value, uint256 length)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
                // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
                // We add 0x20 to the total and round down to a multiple of 0x20.
                // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
                result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
                mstore(0x40, add(result, 0x20)) // Allocate memory.
                mstore(result, 0) // Zeroize the slot after the string.
    
                let end := result // Cache the end to calculate the length later.
                // Store "0123456789abcdef" in scratch space.
                mstore(0x0f, 0x30313233343536373839616263646566)
    
                let start := sub(result, add(length, length))
                let w := not(1) // Tsk.
                let temp := value
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                for {} 1 {} {
                    result := add(result, w) // `sub(result, 2)`.
                    mstore8(add(result, 1), mload(and(temp, 15)))
                    mstore8(result, mload(and(shr(4, temp), 15)))
                    temp := shr(8, temp)
                    if iszero(xor(result, start)) { break }
                }
                if temp {
                    mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                    revert(0x1c, 0x04)
                }
                let n := sub(end, result)
                result := sub(result, 0x20)
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
        /// As address are 20 bytes long, the output will left-padded to have
        /// a length of `20 * 2 + 2` bytes.
        function toHexString(uint256 value) internal pure returns (string memory result) {
            result = toHexStringNoPrefix(value);
            /// @solidity memory-safe-assembly
            assembly {
                let n := add(mload(result), 2) // Compute the length.
                mstore(result, 0x3078) // Store the "0x" prefix.
                result := sub(result, 2) // Move the pointer.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x".
        /// The output excludes leading "0" from the `toHexString` output.
        /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
        function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
            result = toHexStringNoPrefix(value);
            /// @solidity memory-safe-assembly
            assembly {
                let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
                let n := add(mload(result), 2) // Compute the length.
                mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
                result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
                mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
        /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
        function toMinimalHexStringNoPrefix(uint256 value)
            internal
            pure
            returns (string memory result)
        {
            result = toHexStringNoPrefix(value);
            /// @solidity memory-safe-assembly
            assembly {
                let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
                let n := mload(result) // Get the length.
                result := add(result, o) // Move the pointer, accounting for leading zero.
                mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is encoded using 2 hexadecimal digits per byte.
        /// As address are 20 bytes long, the output will left-padded to have
        /// a length of `20 * 2` bytes.
        function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
                // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
                result := add(mload(0x40), 0x80)
                mstore(0x40, add(result, 0x20)) // Allocate memory.
                mstore(result, 0) // Zeroize the slot after the string.
    
                let end := result // Cache the end to calculate the length later.
                mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
    
                let w := not(1) // Tsk.
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                for { let temp := value } 1 {} {
                    result := add(result, w) // `sub(result, 2)`.
                    mstore8(add(result, 1), mload(and(temp, 15)))
                    mstore8(result, mload(and(shr(4, temp), 15)))
                    temp := shr(8, temp)
                    if iszero(temp) { break }
                }
                let n := sub(end, result)
                result := sub(result, 0x20)
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
        /// and the alphabets are capitalized conditionally according to
        /// https://eips.ethereum.org/EIPS/eip-55
        function toHexStringChecksummed(address value) internal pure returns (string memory result) {
            result = toHexString(value);
            /// @solidity memory-safe-assembly
            assembly {
                let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                let o := add(result, 0x22)
                let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                let t := shl(240, 136) // `0b10001000 << 240`
                for { let i := 0 } 1 {} {
                    mstore(add(i, i), mul(t, byte(i, hashed)))
                    i := add(i, 1)
                    if eq(i, 20) { break }
                }
                mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                o := add(o, 0x20)
                mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
        function toHexString(address value) internal pure returns (string memory result) {
            result = toHexStringNoPrefix(value);
            /// @solidity memory-safe-assembly
            assembly {
                let n := add(mload(result), 2) // Compute the length.
                mstore(result, 0x3078) // Store the "0x" prefix.
                result := sub(result, 2) // Move the pointer.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                // Allocate memory.
                // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                mstore(0x40, add(result, 0x80))
                mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
    
                result := add(result, 2)
                mstore(result, 40) // Store the length.
                let o := add(result, 0x20)
                mstore(add(o, 40), 0) // Zeroize the slot after the string.
                value := shl(96, value)
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                for { let i := 0 } 1 {} {
                    let p := add(o, add(i, i))
                    let temp := byte(i, value)
                    mstore8(add(p, 1), mload(and(temp, 15)))
                    mstore8(p, mload(shr(4, temp)))
                    i := add(i, 1)
                    if eq(i, 20) { break }
                }
            }
        }
    
        /// @dev Returns the hex encoded string from the raw bytes.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexString(bytes memory raw) internal pure returns (string memory result) {
            result = toHexStringNoPrefix(raw);
            /// @solidity memory-safe-assembly
            assembly {
                let n := add(mload(result), 2) // Compute the length.
                mstore(result, 0x3078) // Store the "0x" prefix.
                result := sub(result, 2) // Move the pointer.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the hex encoded string from the raw bytes.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                let n := mload(raw)
                result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                mstore(result, add(n, n)) // Store the length of the output.
    
                mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
                let o := add(result, 0x20)
                let end := add(raw, n)
                for {} iszero(eq(raw, end)) {} {
                    raw := add(raw, 1)
                    mstore8(add(o, 1), mload(and(mload(raw), 15)))
                    mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                    o := add(o, 2)
                }
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   RUNE STRING OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the number of UTF characters in the string.
        function runeCount(string memory s) internal pure returns (uint256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(s) {
                    mstore(0x00, div(not(0), 255))
                    mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                    let o := add(s, 0x20)
                    let end := add(o, mload(s))
                    for { result := 1 } 1 { result := add(result, 1) } {
                        o := add(o, byte(0, mload(shr(250, mload(o)))))
                        if iszero(lt(o, end)) { break }
                    }
                }
            }
        }
    
        /// @dev Returns if this string is a 7-bit ASCII string.
        /// (i.e. all characters codes are in [0..127])
        function is7BitASCII(string memory s) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let mask := shl(7, div(not(0), 255))
                let n := mload(s)
                if n {
                    let o := add(s, 0x20)
                    let end := add(o, n)
                    let last := mload(end)
                    mstore(end, 0)
                    for {} 1 {} {
                        if and(mask, mload(o)) {
                            result := 0
                            break
                        }
                        o := add(o, 0x20)
                        if iszero(lt(o, end)) { break }
                    }
                    mstore(end, last)
                }
            }
        }
    
        /// @dev Returns if this string is a 7-bit ASCII string,
        /// AND all characters are in the `allowed` lookup.
        /// Note: If `s` is empty, returns true regardless of `allowed`.
        function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                if mload(s) {
                    let allowed_ := shr(128, shl(128, allowed))
                    let o := add(s, 0x20)
                    for { let end := add(o, mload(s)) } 1 {} {
                        result := and(result, shr(byte(0, mload(o)), allowed_))
                        o := add(o, 1)
                        if iszero(and(result, lt(o, end))) { break }
                    }
                }
            }
        }
    
        /// @dev Converts the bytes in the 7-bit ASCII string `s` to
        /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
        /// To save runtime gas, you can cache the result in an immutable variable.
        function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(s) {
                    let o := add(s, 0x20)
                    for { let end := add(o, mload(s)) } 1 {} {
                        result := or(result, shl(byte(0, mload(o)), 1))
                        o := add(o, 1)
                        if iszero(lt(o, end)) { break }
                    }
                    if shr(128, result) {
                        mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   BYTE STRING OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // For performance and bytecode compactness, byte string operations are restricted
        // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
        // Usage of byte string operations on charsets with runes spanning two or more bytes
        // can lead to undefined behavior.
    
        /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
        function replace(string memory subject, string memory needle, string memory replacement)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let needleLen := mload(needle)
                let replacementLen := mload(replacement)
                let d := sub(result, subject) // Memory difference.
                let i := add(subject, 0x20) // Subject bytes pointer.
                let end := add(i, mload(subject))
                if iszero(gt(needleLen, mload(subject))) {
                    let subjectSearchEnd := add(sub(end, needleLen), 1)
                    let h := 0 // The hash of `needle`.
                    if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
                    let s := mload(add(needle, 0x20))
                    for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
                        let t := mload(i)
                        // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
                        if iszero(shr(m, xor(t, s))) {
                            if h {
                                if iszero(eq(keccak256(i, needleLen), h)) {
                                    mstore(add(i, d), t)
                                    i := add(i, 1)
                                    if iszero(lt(i, subjectSearchEnd)) { break }
                                    continue
                                }
                            }
                            // Copy the `replacement` one word at a time.
                            for { let j := 0 } 1 {} {
                                mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
                                j := add(j, 0x20)
                                if iszero(lt(j, replacementLen)) { break }
                            }
                            d := sub(add(d, replacementLen), needleLen)
                            if needleLen {
                                i := add(i, needleLen)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        mstore(add(i, d), t)
                        i := add(i, 1)
                        if iszero(lt(i, subjectSearchEnd)) { break }
                    }
                }
                let n := add(sub(d, add(result, 0x20)), end)
                // Copy the rest of the string one word at a time.
                for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
                let o := add(i, d)
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from left to right, starting from `from`.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function indexOf(string memory subject, string memory needle, uint256 from)
            internal
            pure
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := not(0) // Initialize to `NOT_FOUND`.
                for { let subjectLen := mload(subject) } 1 {} {
                    if iszero(mload(needle)) {
                        result := from
                        if iszero(gt(from, subjectLen)) { break }
                        result := subjectLen
                        break
                    }
                    let needleLen := mload(needle)
                    let subjectStart := add(subject, 0x20)
    
                    subject := add(subjectStart, from)
                    let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
                    let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
                    let s := mload(add(needle, 0x20))
    
                    if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
    
                    if iszero(lt(needleLen, 0x20)) {
                        for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                            if iszero(shr(m, xor(mload(subject), s))) {
                                if eq(keccak256(subject, needleLen), h) {
                                    result := sub(subject, subjectStart)
                                    break
                                }
                            }
                            subject := add(subject, 1)
                            if iszero(lt(subject, end)) { break }
                        }
                        break
                    }
                    for {} 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            result := sub(subject, subjectStart)
                            break
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from left to right.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function indexOf(string memory subject, string memory needle)
            internal
            pure
            returns (uint256 result)
        {
            result = indexOf(subject, needle, 0);
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from right to left, starting from `from`.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function lastIndexOf(string memory subject, string memory needle, uint256 from)
            internal
            pure
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for {} 1 {} {
                    result := not(0) // Initialize to `NOT_FOUND`.
                    let needleLen := mload(needle)
                    if gt(needleLen, mload(subject)) { break }
                    let w := result
    
                    let fromMax := sub(mload(subject), needleLen)
                    if iszero(gt(fromMax, from)) { from := fromMax }
    
                    let end := add(add(subject, 0x20), w)
                    subject := add(add(subject, 0x20), from)
                    if iszero(gt(subject, end)) { break }
                    // As this function is not too often used,
                    // we shall simply use keccak256 for smaller bytecode size.
                    for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                        if eq(keccak256(subject, needleLen), h) {
                            result := sub(subject, add(end, 1))
                            break
                        }
                        subject := add(subject, w) // `sub(subject, 1)`.
                        if iszero(gt(subject, end)) { break }
                    }
                    break
                }
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from right to left.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function lastIndexOf(string memory subject, string memory needle)
            internal
            pure
            returns (uint256 result)
        {
            result = lastIndexOf(subject, needle, type(uint256).max);
        }
    
        /// @dev Returns true if `needle` is found in `subject`, false otherwise.
        function contains(string memory subject, string memory needle) internal pure returns (bool) {
            return indexOf(subject, needle) != NOT_FOUND;
        }
    
        /// @dev Returns whether `subject` starts with `needle`.
        function startsWith(string memory subject, string memory needle)
            internal
            pure
            returns (bool result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let needleLen := mload(needle)
                // Just using keccak256 directly is actually cheaper.
                // forgefmt: disable-next-item
                result := and(
                    iszero(gt(needleLen, mload(subject))),
                    eq(
                        keccak256(add(subject, 0x20), needleLen),
                        keccak256(add(needle, 0x20), needleLen)
                    )
                )
            }
        }
    
        /// @dev Returns whether `subject` ends with `needle`.
        function endsWith(string memory subject, string memory needle)
            internal
            pure
            returns (bool result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let needleLen := mload(needle)
                // Whether `needle` is not longer than `subject`.
                let inRange := iszero(gt(needleLen, mload(subject)))
                // Just using keccak256 directly is actually cheaper.
                // forgefmt: disable-next-item
                result := and(
                    eq(
                        keccak256(
                            // `subject + 0x20 + max(subjectLen - needleLen, 0)`.
                            add(add(subject, 0x20), mul(inRange, sub(mload(subject), needleLen))),
                            needleLen
                        ),
                        keccak256(add(needle, 0x20), needleLen)
                    ),
                    inRange
                )
            }
        }
    
        /// @dev Returns `subject` repeated `times`.
        function repeat(string memory subject, uint256 times)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let subjectLen := mload(subject)
                if iszero(or(iszero(times), iszero(subjectLen))) {
                    result := mload(0x40)
                    subject := add(subject, 0x20)
                    let o := add(result, 0x20)
                    for {} 1 {} {
                        // Copy the `subject` one word at a time.
                        for { let j := 0 } 1 {} {
                            mstore(add(o, j), mload(add(subject, j)))
                            j := add(j, 0x20)
                            if iszero(lt(j, subjectLen)) { break }
                        }
                        o := add(o, subjectLen)
                        times := sub(times, 1)
                        if iszero(times) { break }
                    }
                    mstore(o, 0) // Zeroize the slot after the string.
                    mstore(0x40, add(o, 0x20)) // Allocate memory.
                    mstore(result, sub(o, add(result, 0x20))) // Store the length.
                }
            }
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
        /// `start` and `end` are byte offsets.
        function slice(string memory subject, uint256 start, uint256 end)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let subjectLen := mload(subject)
                if iszero(gt(subjectLen, end)) { end := subjectLen }
                if iszero(gt(subjectLen, start)) { start := subjectLen }
                if lt(start, end) {
                    result := mload(0x40)
                    let n := sub(end, start)
                    let i := add(subject, start)
                    let w := not(0x1f)
                    // Copy the `subject` one word at a time, backwards.
                    for { let j := and(add(n, 0x1f), w) } 1 {} {
                        mstore(add(result, j), mload(add(i, j)))
                        j := add(j, w) // `sub(j, 0x20)`.
                        if iszero(j) { break }
                    }
                    let o := add(add(result, 0x20), n)
                    mstore(o, 0) // Zeroize the slot after the string.
                    mstore(0x40, add(o, 0x20)) // Allocate memory.
                    mstore(result, n) // Store the length.
                }
            }
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
        /// `start` is a byte offset.
        function slice(string memory subject, uint256 start)
            internal
            pure
            returns (string memory result)
        {
            result = slice(subject, start, type(uint256).max);
        }
    
        /// @dev Returns all the indices of `needle` in `subject`.
        /// The indices are byte offsets.
        function indicesOf(string memory subject, string memory needle)
            internal
            pure
            returns (uint256[] memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let searchLen := mload(needle)
                if iszero(gt(searchLen, mload(subject))) {
                    result := mload(0x40)
                    let i := add(subject, 0x20)
                    let o := add(result, 0x20)
                    let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
                    let h := 0 // The hash of `needle`.
                    if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
                    let s := mload(add(needle, 0x20))
                    for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
                        let t := mload(i)
                        // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
                        if iszero(shr(m, xor(t, s))) {
                            if h {
                                if iszero(eq(keccak256(i, searchLen), h)) {
                                    i := add(i, 1)
                                    if iszero(lt(i, subjectSearchEnd)) { break }
                                    continue
                                }
                            }
                            mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
                            o := add(o, 0x20)
                            i := add(i, searchLen) // Advance `i` by `searchLen`.
                            if searchLen {
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        i := add(i, 1)
                        if iszero(lt(i, subjectSearchEnd)) { break }
                    }
                    mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
                    // Allocate memory for result.
                    // We allocate one more word, so this array can be recycled for {split}.
                    mstore(0x40, add(o, 0x20))
                }
            }
        }
    
        /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
        function split(string memory subject, string memory delimiter)
            internal
            pure
            returns (string[] memory result)
        {
            uint256[] memory indices = indicesOf(subject, delimiter);
            /// @solidity memory-safe-assembly
            assembly {
                let w := not(0x1f)
                let indexPtr := add(indices, 0x20)
                let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                mstore(add(indicesEnd, w), mload(subject))
                mstore(indices, add(mload(indices), 1))
                for { let prevIndex := 0 } 1 {} {
                    let index := mload(indexPtr)
                    mstore(indexPtr, 0x60)
                    if iszero(eq(index, prevIndex)) {
                        let element := mload(0x40)
                        let l := sub(index, prevIndex)
                        mstore(element, l) // Store the length of the element.
                        // Copy the `subject` one word at a time, backwards.
                        for { let o := and(add(l, 0x1f), w) } 1 {} {
                            mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                            o := add(o, w) // `sub(o, 0x20)`.
                            if iszero(o) { break }
                        }
                        mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the string.
                        // Allocate memory for the length and the bytes, rounded up to a multiple of 32.
                        mstore(0x40, add(element, and(add(l, 0x3f), w)))
                        mstore(indexPtr, element) // Store the `element` into the array.
                    }
                    prevIndex := add(index, mload(delimiter))
                    indexPtr := add(indexPtr, 0x20)
                    if iszero(lt(indexPtr, indicesEnd)) { break }
                }
                result := indices
                if iszero(mload(delimiter)) {
                    result := add(indices, 0x20)
                    mstore(result, sub(mload(indices), 2))
                }
            }
        }
    
        /// @dev Returns a concatenated string of `a` and `b`.
        /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
        function concat(string memory a, string memory b)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let w := not(0x1f)
                let aLen := mload(a)
                // Copy `a` one word at a time, backwards.
                for { let o := and(add(aLen, 0x20), w) } 1 {} {
                    mstore(add(result, o), mload(add(a, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                let bLen := mload(b)
                let output := add(result, aLen)
                // Copy `b` one word at a time, backwards.
                for { let o := and(add(bLen, 0x20), w) } 1 {} {
                    mstore(add(output, o), mload(add(b, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                let totalLen := add(aLen, bLen)
                let last := add(add(result, 0x20), totalLen)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(result, totalLen) // Store the length.
                mstore(0x40, add(last, 0x20)) // Allocate memory.
            }
        }
    
        /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
        /// WARNING! This function is only compatible with 7-bit ASCII strings.
        function toCase(string memory subject, bool toUpper)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let n := mload(subject)
                if n {
                    result := mload(0x40)
                    let o := add(result, 0x20)
                    let d := sub(subject, result)
                    let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                    for { let end := add(o, n) } 1 {} {
                        let b := byte(0, mload(add(d, o)))
                        mstore8(o, xor(and(shr(b, flags), 0x20), b))
                        o := add(o, 1)
                        if eq(o, end) { break }
                    }
                    mstore(result, n) // Store the length.
                    mstore(o, 0) // Zeroize the slot after the string.
                    mstore(0x40, add(o, 0x20)) // Allocate memory.
                }
            }
        }
    
        /// @dev Returns a string from a small bytes32 string.
        /// `s` must be null-terminated, or behavior will be undefined.
        function fromSmallString(bytes32 s) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let n := 0
                for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
                mstore(result, n) // Store the length.
                let o := add(result, 0x20)
                mstore(o, s) // Store the bytes of the string.
                mstore(add(o, n), 0) // Zeroize the slot after the string.
                mstore(0x40, add(result, 0x40)) // Allocate memory.
            }
        }
    
        /// @dev Returns the small string, with all bytes after the first null byte zeroized.
        function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
                mstore(0x00, s)
                mstore(result, 0x00)
                result := mload(0x00)
            }
        }
    
        /// @dev Returns the string as a normalized null-terminated small string.
        function toSmallString(string memory s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(s)
                if iszero(lt(result, 33)) {
                    mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                    revert(0x1c, 0x04)
                }
                result := shl(shl(3, sub(32, result)), mload(add(s, result)))
            }
        }
    
        /// @dev Returns a lowercased copy of the string.
        /// WARNING! This function is only compatible with 7-bit ASCII strings.
        function lower(string memory subject) internal pure returns (string memory result) {
            result = toCase(subject, false);
        }
    
        /// @dev Returns an UPPERCASED copy of the string.
        /// WARNING! This function is only compatible with 7-bit ASCII strings.
        function upper(string memory subject) internal pure returns (string memory result) {
            result = toCase(subject, true);
        }
    
        /// @dev Escapes the string to be used within HTML tags.
        function escapeHTML(string memory s) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let end := add(s, mload(s))
                let o := add(result, 0x20)
                // Store the bytes of the packed offsets and strides into the scratch space.
                // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                mstore(0x1f, 0x900094)
                mstore(0x08, 0xc0000000a6ab)
                // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
                for {} iszero(eq(s, end)) {} {
                    s := add(s, 1)
                    let c := and(mload(s), 0xff)
                    // Not in `["\"","'","&","<",">"]`.
                    if iszero(and(shl(c, 1), 0x500000c400000000)) {
                        mstore8(o, c)
                        o := add(o, 1)
                        continue
                    }
                    let t := shr(248, mload(c))
                    mstore(o, mload(and(t, 0x1f)))
                    o := add(o, shr(5, t))
                }
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(result, sub(o, add(result, 0x20))) // Store the length.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
            }
        }
    
        /// @dev Escapes the string to be used within double-quotes in a JSON.
        /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
        function escapeJSON(string memory s, bool addDoubleQuotes)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let o := add(result, 0x20)
                if addDoubleQuotes {
                    mstore8(o, 34)
                    o := add(1, o)
                }
                // Store "\\u0000" in scratch space.
                // Store "0123456789abcdef" in scratch space.
                // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                // into the scratch space.
                mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                // Bitmask for detecting `["\"","\\"]`.
                let e := or(shl(0x22, 1), shl(0x5c, 1))
                for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                    s := add(s, 1)
                    let c := and(mload(s), 0xff)
                    if iszero(lt(c, 0x20)) {
                        if iszero(and(shl(c, 1), e)) {
                            // Not in `["\"","\\"]`.
                            mstore8(o, c)
                            o := add(o, 1)
                            continue
                        }
                        mstore8(o, 0x5c) // "\\".
                        mstore8(add(o, 1), c)
                        o := add(o, 2)
                        continue
                    }
                    if iszero(and(shl(c, 1), 0x3700)) {
                        // Not in `["\b","\t","\n","\f","\d"]`.
                        mstore8(0x1d, mload(shr(4, c))) // Hex value.
                        mstore8(0x1e, mload(and(c, 15))) // Hex value.
                        mstore(o, mload(0x19)) // "\\u00XX".
                        o := add(o, 6)
                        continue
                    }
                    mstore8(o, 0x5c) // "\\".
                    mstore8(add(o, 1), mload(add(c, 8)))
                    o := add(o, 2)
                }
                if addDoubleQuotes {
                    mstore8(o, 34)
                    o := add(1, o)
                }
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(result, sub(o, add(result, 0x20))) // Store the length.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
            }
        }
    
        /// @dev Escapes the string to be used within double-quotes in a JSON.
        function escapeJSON(string memory s) internal pure returns (string memory result) {
            result = escapeJSON(s, false);
        }
    
        /// @dev Encodes `s` so that it can be safely used in a URI,
        /// just like `encodeURIComponent` in JavaScript.
        /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
        /// See: https://datatracker.ietf.org/doc/html/rfc2396
        /// See: https://datatracker.ietf.org/doc/html/rfc3986
        function encodeURIComponent(string memory s) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                // Store "0123456789ABCDEF" in scratch space.
                // Uppercased to be consistent with JavaScript's implementation.
                mstore(0x0f, 0x30313233343536373839414243444546)
                let o := add(result, 0x20)
                for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                    s := add(s, 1)
                    let c := and(mload(s), 0xff)
                    // If not in `[0-9A-Z-a-z-_.!~*'()]`.
                    if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
                        mstore8(o, 0x25) // '%'.
                        mstore8(add(o, 1), mload(and(shr(4, c), 15)))
                        mstore8(add(o, 2), mload(and(c, 15)))
                        o := add(o, 3)
                        continue
                    }
                    mstore8(o, c)
                    o := add(o, 1)
                }
                mstore(result, sub(o, add(result, 0x20))) // Store the length.
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
            }
        }
    
        /// @dev Returns whether `a` equals `b`.
        function eq(string memory a, string memory b) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
            }
        }
    
        /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
        function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                // These should be evaluated on compile time, as far as possible.
                let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
                let x := not(or(m, or(b, add(m, and(b, m)))))
                let r := shl(7, iszero(iszero(shr(128, x))))
                r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                r := or(r, shl(4, lt(0xffff, shr(r, x))))
                r := or(r, shl(3, lt(0xff, shr(r, x))))
                // forgefmt: disable-next-item
                result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                    xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
            }
        }
    
        /// @dev Packs a single string with its length into a single word.
        /// Returns `bytes32(0)` if the length is zero or greater than 31.
        function packOne(string memory a) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                // We don't need to zero right pad the string,
                // since this is our own custom non-standard packing scheme.
                result :=
                    mul(
                        // Load the length and the bytes.
                        mload(add(a, 0x1f)),
                        // `length != 0 && length < 32`. Abuses underflow.
                        // Assumes that the length is valid and within the block gas limit.
                        lt(sub(mload(a), 1), 0x1f)
                    )
            }
        }
    
        /// @dev Unpacks a string packed using {packOne}.
        /// Returns the empty string if `packed` is `bytes32(0)`.
        /// If `packed` is not an output of {packOne}, the output behavior is undefined.
        function unpackOne(bytes32 packed) internal pure returns (string memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40) // Grab the free memory pointer.
                mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
                mstore(result, 0) // Zeroize the length slot.
                mstore(add(result, 0x1f), packed) // Store the length and bytes.
                mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
            }
        }
    
        /// @dev Packs two strings with their lengths into a single word.
        /// Returns `bytes32(0)` if combined length is zero or greater than 30.
        function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let aLen := mload(a)
                // We don't need to zero right pad the strings,
                // since this is our own custom non-standard packing scheme.
                result :=
                    mul(
                        or( // Load the length and the bytes of `a` and `b`.
                        shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
                        // `totalLen != 0 && totalLen < 31`. Abuses underflow.
                        // Assumes that the lengths are valid and within the block gas limit.
                        lt(sub(add(aLen, mload(b)), 1), 0x1e)
                    )
            }
        }
    
        /// @dev Unpacks strings packed using {packTwo}.
        /// Returns the empty strings if `packed` is `bytes32(0)`.
        /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
        function unpackTwo(bytes32 packed)
            internal
            pure
            returns (string memory resultA, string memory resultB)
        {
            /// @solidity memory-safe-assembly
            assembly {
                resultA := mload(0x40) // Grab the free memory pointer.
                resultB := add(resultA, 0x40)
                // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
                mstore(0x40, add(resultB, 0x40))
                // Zeroize the length slots.
                mstore(resultA, 0)
                mstore(resultB, 0)
                // Store the lengths and bytes.
                mstore(add(resultA, 0x1f), packed)
                mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
                // Right pad with zeroes.
                mstore(add(add(resultA, 0x20), mload(resultA)), 0)
                mstore(add(add(resultB, 0x20), mload(resultB)), 0)
            }
        }
    
        /// @dev Directly returns `a` without copying.
        function directReturn(string memory a) internal pure {
            assembly {
                // Assumes that the string does not start from the scratch space.
                let retStart := sub(a, 0x20)
                let retUnpaddedSize := add(mload(a), 0x40)
                // Right pad with zeroes. Just in case the string is produced
                // by a method that doesn't zero right pad.
                mstore(add(retStart, retUnpaddedSize), 0)
                mstore(retStart, 0x20) // Store the return offset.
                // End the transaction, returning the string.
                return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
            }
        }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
    library MerkleProofLib {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*            MERKLE PROOF VERIFICATION OPERATIONS            */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(proof) {
                    // Initialize `offset` to the offset of `proof` elements in memory.
                    let offset := add(proof, 0x20)
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(offset, shl(5, mload(proof)))
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, mload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), mload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
    
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if proof.length {
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(proof.offset, shl(5, proof.length))
                    // Initialize `offset` to the offset of `proof` in the calldata.
                    let offset := proof.offset
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, calldataload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), calldataload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
    
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - The sum of the lengths of `proof` and `leaves` must never overflow.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The memory offset of `proof` must be non-zero
        ///   (i.e. `proof` is not pointing to the scratch space).
        function verifyMultiProof(
            bytes32[] memory proof,
            bytes32 root,
            bytes32[] memory leaves,
            bool[] memory flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // Cache the lengths of the arrays.
                let leavesLength := mload(leaves)
                let proofLength := mload(proof)
                let flagsLength := mload(flags)
    
                // Advance the pointers of the arrays to point to the data.
                leaves := add(0x20, leaves)
                proof := add(0x20, proof)
                flags := add(0x20, flags)
    
                // If the number of flags is correct.
                for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flagsLength) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                        break
                    }
    
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof, shl(5, proofLength))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    leavesLength := shl(5, leavesLength)
                    for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                        mstore(add(hashesFront, i), mload(add(leaves, i)))
                    }
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, leavesLength)
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flagsLength := add(hashesBack, shl(5, flagsLength))
    
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
    
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(mload(flags)) {
                            // Loads the next proof.
                            b := mload(proof)
                            proof := add(proof, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
    
                        // Advance to the next flag.
                        flags := add(flags, 0x20)
    
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flagsLength)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof)
                        )
                    break
                }
            }
        }
    
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The calldata offset of `proof` must be non-zero
        ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
        function verifyMultiProofCalldata(
            bytes32[] calldata proof,
            bytes32 root,
            bytes32[] calldata leaves,
            bool[] calldata flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // If the number of flags is correct.
                for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flags.length) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        // forgefmt: disable-next-item
                        isValid := eq(
                            calldataload(
                                xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                            ),
                            root
                        )
                        break
                    }
    
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof.offset, shl(5, proof.length))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, shl(5, leaves.length))
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flags.length := add(hashesBack, shl(5, flags.length))
    
                    // We don't need to make a copy of `proof.offset` or `flags.offset`,
                    // as they are pass-by-value (this trick may not always save gas).
    
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
    
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(calldataload(flags.offset)) {
                            // Loads the next proof.
                            b := calldataload(proof.offset)
                            proof.offset := add(proof.offset, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
    
                        // Advance to the next flag offset.
                        flags.offset := add(flags.offset, 0x20)
    
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flags.length)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof.offset)
                        )
                    break
                }
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an empty calldata bytes32 array.
        function emptyProof() internal pure returns (bytes32[] calldata proof) {
            /// @solidity memory-safe-assembly
            assembly {
                proof.length := 0
            }
        }
    
        /// @dev Returns an empty calldata bytes32 array.
        function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
            /// @solidity memory-safe-assembly
            assembly {
                leaves.length := 0
            }
        }
    
        /// @dev Returns an empty calldata bool array.
        function emptyFlags() internal pure returns (bool[] calldata flags) {
            /// @solidity memory-safe-assembly
            assembly {
                flags.length := 0
            }
        }
    }

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

    Context size (optional):