ETH Price: $2,697.37 (+8.67%)

Transaction Decoder

Block:
21742176 at Jan-31-2025 04:37:59 AM +UTC
Transaction Fee:
0.001719717777617528 ETH $4.64
Gas Used:
1,419,052 Gas / 1.211877914 Gwei

Emitted Events:

238 TWCloneFactory.ProxyDeployed( implementation=ERC721CoreInitializable, proxy=ERC721CoreInitializable, deployer=[Sender] 0x8917370fac5ef51ffc2181d682a9d79761164bfa )
239 ERC721CoreInitializable.ContractURIUpdated( )
240 ERC721CoreInitializable.OwnershipTransferred( oldOwner=0x00000000...000000000, newOwner=[Sender] 0x8917370fac5ef51ffc2181d682a9d79761164bfa )
241 ERC721CoreInitializable.ModuleInstalled( caller=[Receiver] TWCloneFactory, implementation=0xe96742b5cf7302b8a1bd348248515c403696bb14, installedModule=0xe96742b5cf7302b8a1bd348248515c403696bb14 )
242 ERC721CoreInitializable.ModuleInstalled( caller=[Receiver] TWCloneFactory, implementation=0xe285ac0ad6addd85f6e41743690500026d7921c0, installedModule=0xe285ac0ad6addd85f6e41743690500026d7921c0 )
243 ERC721CoreInitializable.0x5ba7b74d84f5acf3db282044fe19f114c13c57cd592cbe0931b18cde4bfe6101( 0x5ba7b74d84f5acf3db282044fe19f114c13c57cd592cbe0931b18cde4bfe6101, 0x0000000000000000000000008917370fac5ef51ffc2181d682a9d79761164bfa, 0000000000000000000000000000000000000000000000000000000000000000 )
244 ERC721CoreInitializable.0xcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac( 0xcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
245 ERC721CoreInitializable.ModuleInstalled( caller=[Receiver] TWCloneFactory, implementation=0xe258f07518aed001f5a6ca386e4c65f216845e20, installedModule=0xe258f07518aed001f5a6ca386e4c65f216845e20 )
246 ERC721CoreInitializable.Initialized( version=1 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
11.065178123759340904 Eth11.065179684716540904 Eth0.0000015609572
0x76F948E5...Bf524805E
0x8917370F...761164Bfa
0.055195242579807427 Eth
Nonce: 6
0.053475524802189899 Eth
Nonce: 7
0.001719717777617528
0xFD14F2a4...5A5FE9Db8
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 497590261154554171967157591673549152399287388940340935937895353665981312784496646756394985331912284160482291

Execution Trace

TWCloneFactory.deployProxyByImplementation( _implementation=0xc75F0EA874c8f7A9493CA9D5A7d354359bF0e79b, _data=0x62835ADE00000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000008917370FAC5EF51FFC2181D682A9D79761164BFA00000000000000000000000000000000000000000000000000000000000001A000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000009424C41434B4841574B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004444F574E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733A2F2F516D6170677478696A694E78366645454C4E6F376261524C37596A5A653235317357456578446E5A5A6F4D5A70692F300000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000E96742B5CF7302B8A1BD348248515C403696BB14000000000000000000000000E285AC0AD6ADDD85F6E41743690500026D7921C0000000000000000000000000E258F07518AED001F5A6CA386E4C65F216845E200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008917370FAC5EF51FFC2181D682A9D79761164BFA000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000008917370FAC5EF51FFC2181D682A9D79761164BFA00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, _salt=00000000000000000000000000000000000000000000000000000000014BC25C ) => ( deployedProxy=0xFD14F2a4d0892ee2d2DD0049e2482725A5FE9Db8 )
  • ERC721CoreInitializable.3d602d80( )
  • ERC721CoreInitializable.initialize( _name=BLACKHAWK, _symbol=DOWN, _contractURI=ipfs://QmapgtxijiNx6fEELNo7baRL7YjZe251sWEexDnZZoMZpi/0, _owner=0x8917370FAC5EF51fFC2181d682A9d79761164Bfa, _modules=[0xE96742b5CF7302B8a1Bd348248515C403696Bb14, 0xE285AC0AD6addD85f6e41743690500026d7921c0, 0xE258f07518AED001F5a6cA386E4C65F216845e20], _moduleInstallData=[AAAAAAAAAAAAAAAAiRc3D6xe9R/8IYHWgqnXl2EWS/o=, , AAAAAAAAAAAAAAAAiRc3D6xe9R/8IYHWgqnXl2EWS/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA] )
    • ERC721CoreInitializable.initialize( _name=BLACKHAWK, _symbol=DOWN, _contractURI=ipfs://QmapgtxijiNx6fEELNo7baRL7YjZe251sWEexDnZZoMZpi/0, _owner=0x8917370FAC5EF51fFC2181d682A9d79761164Bfa, _modules=[0xE96742b5CF7302B8a1Bd348248515C403696Bb14, 0xE285AC0AD6addD85f6e41743690500026d7921c0, 0xE258f07518AED001F5a6cA386E4C65F216845e20], _moduleInstallData=[AAAAAAAAAAAAAAAAiRc3D6xe9R/8IYHWgqnXl2EWS/o=, , AAAAAAAAAAAAAAAAiRc3D6xe9R/8IYHWgqnXl2EWS/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA] )
      • 0xe96742b5cf7302b8a1bd348248515c403696bb14.STATICCALL( )
      • 0xe96742b5cf7302b8a1bd348248515c403696bb14.6d61fe70( )
      • 0xe285ac0ad6addd85f6e41743690500026d7921c0.STATICCALL( )
      • 0xe258f07518aed001f5a6ca386e4c65f216845e20.STATICCALL( )
      • 0xe258f07518aed001f5a6ca386e4c65f216845e20.6d61fe70( )
        File 1 of 3: TWCloneFactory
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.11;
        /// @author thirdweb
        //   $$\\     $$\\       $$\\                 $$\\                         $$\\
        //   $$ |    $$ |      \\__|                $$ |                        $$ |
        // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
        // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
        //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
        //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
        //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
        //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
        import "./extension/interface/IContractFactory.sol";
        import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
        import "@openzeppelin/contracts/utils/Multicall.sol";
        import "@openzeppelin/contracts/proxy/Clones.sol";
        contract TWCloneFactory is Multicall, ERC2771Context, IContractFactory {
            /// @dev Emitted when a proxy is deployed.
            event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer);
            constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {}
            /// @dev Deploys a proxy that points to the given implementation.
            function deployProxyByImplementation(
                address _implementation,
                bytes memory _data,
                bytes32 _salt
            ) public override returns (address deployedProxy) {
                bytes32 salthash = keccak256(abi.encodePacked(_msgSender(), _salt));
                deployedProxy = Clones.cloneDeterministic(_implementation, salthash);
                emit ProxyDeployed(_implementation, deployedProxy, _msgSender());
                if (_data.length > 0) {
                    // slither-disable-next-line unused-return
                    Address.functionCall(deployedProxy, _data);
                }
            }
            function _msgSender() internal view virtual override returns (address sender) {
                return ERC2771Context._msgSender();
            }
            function _msgData() internal view virtual override returns (bytes calldata) {
                return ERC2771Context._msgData();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.0;
        /// @author thirdweb
        interface IContractFactory {
            /**
             *  @notice Deploys a proxy that points to that points to the given implementation.
             *
             *  @param implementation           Address of the implementation to point to.
             *
             *  @param data                     Additional data to pass to the proxy constructor or any other data useful during deployement.
             *  @param salt                     Salt to use for the deterministic address generation.
             */
            function deployProxyByImplementation(
                address implementation,
                bytes memory data,
                bytes32 salt
            ) external returns (address);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
        pragma solidity ^0.8.9;
        import "../utils/Context.sol";
        /**
         * @dev Context variant with ERC2771 support.
         */
        abstract contract ERC2771Context is Context {
            /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
            address private immutable _trustedForwarder;
            /// @custom:oz-upgrades-unsafe-allow constructor
            constructor(address trustedForwarder) {
                _trustedForwarder = trustedForwarder;
            }
            function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                return forwarder == _trustedForwarder;
            }
            function _msgSender() internal view virtual override returns (address sender) {
                if (isTrustedForwarder(msg.sender)) {
                    // The assembly code is more direct than the Solidity version using `abi.decode`.
                    /// @solidity memory-safe-assembly
                    assembly {
                        sender := shr(96, calldataload(sub(calldatasize(), 20)))
                    }
                } else {
                    return super._msgSender();
                }
            }
            function _msgData() internal view virtual override returns (bytes calldata) {
                if (isTrustedForwarder(msg.sender)) {
                    return msg.data[:msg.data.length - 20];
                } else {
                    return super._msgData();
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v4.7.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 {
                    let ptr := mload(0x40)
                    mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                    mstore(add(ptr, 0x14), shl(0x60, implementation))
                    mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                    instance := create(0, ptr, 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 {
                    let ptr := mload(0x40)
                    mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                    mstore(add(ptr, 0x14), shl(0x60, implementation))
                    mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                    instance := create2(0, ptr, 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(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                    mstore(add(ptr, 0x14), shl(0x60, implementation))
                    mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
                    mstore(add(ptr, 0x38), shl(0x60, deployer))
                    mstore(add(ptr, 0x4c), salt)
                    mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                    predicted := keccak256(add(ptr, 0x37), 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.7.0) (utils/Address.sol)
        pragma solidity ^0.8.1;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             *
             * [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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
                require(isContract(target), "Address: call to non-contract");
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResult(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) {
                require(isContract(target), "Address: static call to non-contract");
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResult(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) {
                require(isContract(target), "Address: delegate call to non-contract");
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
            /**
             * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
             * revert reason 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 {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
        pragma solidity ^0.8.0;
        /**
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract Context {
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
        pragma solidity ^0.8.0;
        import "./Address.sol";
        /**
         * @dev Provides a function to batch together multiple calls in a single external call.
         *
         * _Available since v4.1._
         */
        abstract contract Multicall {
            /**
             * @dev Receives and executes a batch of function calls on this contract.
             */
            function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                results = new bytes[](data.length);
                for (uint256 i = 0; i < data.length; i++) {
                    results[i] = Address.functionDelegateCall(address(this), data[i]);
                }
                return results;
            }
        }
        

        File 2 of 3: ERC721CoreInitializable
        // 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
        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
        // 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
        // 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
        pragma solidity ^0.8.4;
        /// @notice Simple single owner authorization mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
        ///
        /// @dev Note:
        /// This implementation does NOT auto-initialize the owner to `msg.sender`.
        /// You MUST call the `_initializeOwner` in the constructor / initializer.
        ///
        /// While the ownable portion follows
        /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
        /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
        abstract contract Ownable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The caller is not authorized to call the function.
            error Unauthorized();
            /// @dev The `newOwner` cannot be the zero address.
            error NewOwnerIsZeroAddress();
            /// @dev The `pendingOwner` does not have a valid handover request.
            error NoHandoverRequest();
            /// @dev Cannot double-initialize.
            error AlreadyInitialized();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
            /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
            /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
            /// despite it not being as lightweight as a single argument event.
            event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
            /// @dev An ownership handover to `pendingOwner` has been requested.
            event OwnershipHandoverRequested(address indexed pendingOwner);
            /// @dev The ownership handover to `pendingOwner` has been canceled.
            event OwnershipHandoverCanceled(address indexed pendingOwner);
            /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
            uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
                0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
            /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
            uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
                0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
            /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
            uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
                0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The owner slot is given by:
            /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
            /// It is intentionally chosen to be a high value
            /// to avoid collision with lower slots.
            /// The choice of manual storage layout is to enable compatibility
            /// with both regular and upgradeable contracts.
            bytes32 internal constant _OWNER_SLOT =
                0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
            /// The ownership handover slot of `newOwner` is given by:
            /// ```
            ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
            ///     let handoverSlot := keccak256(0x00, 0x20)
            /// ```
            /// It stores the expiry timestamp of the two-step ownership handover.
            uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     INTERNAL FUNCTIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
            function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
            /// @dev Initializes the owner directly without authorization guard.
            /// This function must be called upon initialization,
            /// regardless of whether the contract is upgradeable or not.
            /// This is to enable generalization to both regular and upgradeable contracts,
            /// and to save gas in case the initial owner is not the caller.
            /// For performance reasons, this function will not check if there
            /// is an existing owner.
            function _initializeOwner(address newOwner) internal virtual {
                if (_guardInitializeOwner()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        if sload(ownerSlot) {
                            mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                            revert(0x1c, 0x04)
                        }
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Store the new value.
                        sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                    }
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Store the new value.
                        sstore(_OWNER_SLOT, newOwner)
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                    }
                }
            }
            /// @dev Sets the owner directly without authorization guard.
            function _setOwner(address newOwner) internal virtual {
                if (_guardInitializeOwner()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                        // Store the new value.
                        sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                    }
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                        // Store the new value.
                        sstore(ownerSlot, newOwner)
                    }
                }
            }
            /// @dev Throws if the sender is not the owner.
            function _checkOwner() internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // If the caller is not the stored owner, revert.
                    if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Returns how long a two-step ownership handover is valid for in seconds.
            /// Override to return a different value if needed.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
                return 48 * 3600;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  PUBLIC UPDATE FUNCTIONS                   */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Allows the owner to transfer the ownership to `newOwner`.
            function transferOwnership(address newOwner) public payable virtual onlyOwner {
                /// @solidity memory-safe-assembly
                assembly {
                    if iszero(shl(96, newOwner)) {
                        mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                        revert(0x1c, 0x04)
                    }
                }
                _setOwner(newOwner);
            }
            /// @dev Allows the owner to renounce their ownership.
            function renounceOwnership() public payable virtual onlyOwner {
                _setOwner(address(0));
            }
            /// @dev Request a two-step ownership handover to the caller.
            /// The request will automatically expire in 48 hours (172800 seconds) by default.
            function requestOwnershipHandover() public payable virtual {
                unchecked {
                    uint256 expires = block.timestamp + _ownershipHandoverValidFor();
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute and set the handover slot to `expires`.
                        mstore(0x0c, _HANDOVER_SLOT_SEED)
                        mstore(0x00, caller())
                        sstore(keccak256(0x0c, 0x20), expires)
                        // Emit the {OwnershipHandoverRequested} event.
                        log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                    }
                }
            }
            /// @dev Cancels the two-step ownership handover to the caller, if any.
            function cancelOwnershipHandover() public payable virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute and set the handover slot to 0.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, caller())
                    sstore(keccak256(0x0c, 0x20), 0)
                    // Emit the {OwnershipHandoverCanceled} event.
                    log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
                }
            }
            /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
            /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
            function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute and set the handover slot to 0.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, pendingOwner)
                    let handoverSlot := keccak256(0x0c, 0x20)
                    // If the handover does not exist, or has expired.
                    if gt(timestamp(), sload(handoverSlot)) {
                        mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                        revert(0x1c, 0x04)
                    }
                    // Set the handover slot to 0.
                    sstore(handoverSlot, 0)
                }
                _setOwner(pendingOwner);
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   PUBLIC READ FUNCTIONS                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the owner of the contract.
            function owner() public view virtual returns (address result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := sload(_OWNER_SLOT)
                }
            }
            /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
            function ownershipHandoverExpiresAt(address pendingOwner)
                public
                view
                virtual
                returns (uint256 result)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the handover slot.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, pendingOwner)
                    // Load the handover slot.
                    result := sload(keccak256(0x0c, 0x20))
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         MODIFIERS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Marks a function as only callable by the owner.
            modifier onlyOwner() virtual {
                _checkOwner();
                _;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        import {Ownable} from "./Ownable.sol";
        /// @notice Simple single owner and multiroles authorization mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
        /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
        /// for compatibility, the nomenclature for the 2-step ownership handover and roles
        /// may be unique to this codebase.
        abstract contract OwnableRoles is Ownable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The `user`'s roles is updated to `roles`.
            /// Each bit of `roles` represents whether the role is set.
            event RolesUpdated(address indexed user, uint256 indexed roles);
            /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
            uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
                0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The role slot of `user` is given by:
            /// ```
            ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
            ///     let roleSlot := keccak256(0x00, 0x20)
            /// ```
            /// This automatically ignores the upper bits of the `user` in case
            /// they are not clean, as well as keep the `keccak256` under 32-bytes.
            ///
            /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
            uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     INTERNAL FUNCTIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Overwrite the roles directly without authorization guard.
            function _setRoles(address user, uint256 roles) internal virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    // Store the new value.
                    sstore(keccak256(0x0c, 0x20), roles)
                    // Emit the {RolesUpdated} event.
                    log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
                }
            }
            /// @dev Updates the roles directly without authorization guard.
            /// If `on` is true, each set bit of `roles` will be turned on,
            /// otherwise, each set bit of `roles` will be turned off.
            function _updateRoles(address user, uint256 roles, bool on) internal virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    let roleSlot := keccak256(0x0c, 0x20)
                    // Load the current value.
                    let current := sload(roleSlot)
                    // Compute the updated roles if `on` is true.
                    let updated := or(current, roles)
                    // Compute the updated roles if `on` is false.
                    // Use `and` to compute the intersection of `current` and `roles`,
                    // `xor` it with `current` to flip the bits in the intersection.
                    if iszero(on) { updated := xor(current, and(current, roles)) }
                    // Then, store the new value.
                    sstore(roleSlot, updated)
                    // Emit the {RolesUpdated} event.
                    log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
                }
            }
            /// @dev Grants the roles directly without authorization guard.
            /// Each bit of `roles` represents the role to turn on.
            function _grantRoles(address user, uint256 roles) internal virtual {
                _updateRoles(user, roles, true);
            }
            /// @dev Removes the roles directly without authorization guard.
            /// Each bit of `roles` represents the role to turn off.
            function _removeRoles(address user, uint256 roles) internal virtual {
                _updateRoles(user, roles, false);
            }
            /// @dev Throws if the sender does not have any of the `roles`.
            function _checkRoles(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, caller())
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Throws if the sender is not the owner,
            /// and does not have any of the `roles`.
            /// Checks for ownership first, then lazily checks for roles.
            function _checkOwnerOrRoles(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // If the caller is not the stored owner.
                    // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                    if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                        // Compute the role slot.
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, caller())
                        // Load the stored value, and if the `and` intersection
                        // of the value and `roles` is zero, revert.
                        if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            /// @dev Throws if the sender does not have any of the `roles`,
            /// and is not the owner.
            /// Checks for roles first, then lazily checks for ownership.
            function _checkRolesOrOwner(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, caller())
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                        // If the caller is not the stored owner.
                        // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                        if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
            /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
            /// Not recommended to be called on-chain.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
                /// @solidity memory-safe-assembly
                assembly {
                    for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                        // We don't need to mask the values of `ordinals`, as Solidity
                        // cleans dirty upper bits when storing variables into memory.
                        roles := or(shl(mload(add(ordinals, i)), 1), roles)
                    }
                }
            }
            /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
            /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
            /// Not recommended to be called on-chain.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Grab the pointer to the free memory.
                    ordinals := mload(0x40)
                    let ptr := add(ordinals, 0x20)
                    let o := 0
                    // The absence of lookup tables, De Bruijn, etc., here is intentional for
                    // smaller bytecode, as this function is not meant to be called on-chain.
                    for { let t := roles } 1 {} {
                        mstore(ptr, o)
                        // `shr` 5 is equivalent to multiplying by 0x20.
                        // Push back into the ordinals array if the bit is set.
                        ptr := add(ptr, shl(5, and(t, 1)))
                        o := add(o, 1)
                        t := shr(o, roles)
                        if iszero(t) { break }
                    }
                    // Store the length of `ordinals`.
                    mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
                    // Allocate the memory.
                    mstore(0x40, ptr)
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  PUBLIC UPDATE FUNCTIONS                   */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Allows the owner to grant `user` `roles`.
            /// If the `user` already has a role, then it will be an no-op for the role.
            function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
                _grantRoles(user, roles);
            }
            /// @dev Allows the owner to remove `user` `roles`.
            /// If the `user` does not have a role, then it will be an no-op for the role.
            function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
                _removeRoles(user, roles);
            }
            /// @dev Allow the caller to remove their own roles.
            /// If the caller does not have a role, then it will be an no-op for the role.
            function renounceRoles(uint256 roles) public payable virtual {
                _removeRoles(msg.sender, roles);
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   PUBLIC READ FUNCTIONS                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the roles of `user`.
            function rolesOf(address user) public view virtual returns (uint256 roles) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    // Load the stored value.
                    roles := sload(keccak256(0x0c, 0x20))
                }
            }
            /// @dev Returns whether `user` has any of `roles`.
            function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
                return rolesOf(user) & roles != 0;
            }
            /// @dev Returns whether `user` has all of `roles`.
            function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
                return rolesOf(user) & roles == roles;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         MODIFIERS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Marks a function as only callable by an account with `roles`.
            modifier onlyRoles(uint256 roles) virtual {
                _checkRoles(roles);
                _;
            }
            /// @dev Marks a function as only callable by the owner or by an account
            /// with `roles`. Checks for ownership first, then lazily checks for roles.
            modifier onlyOwnerOrRoles(uint256 roles) virtual {
                _checkOwnerOrRoles(roles);
                _;
            }
            /// @dev Marks a function as only callable by an account with `roles`
            /// or the owner. Checks for roles first, then lazily checks for ownership.
            modifier onlyRolesOrOwner(uint256 roles) virtual {
                _checkRolesOrOwner(roles);
                _;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       ROLE CONSTANTS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            // IYKYK
            uint256 internal constant _ROLE_0 = 1 << 0;
            uint256 internal constant _ROLE_1 = 1 << 1;
            uint256 internal constant _ROLE_2 = 1 << 2;
            uint256 internal constant _ROLE_3 = 1 << 3;
            uint256 internal constant _ROLE_4 = 1 << 4;
            uint256 internal constant _ROLE_5 = 1 << 5;
            uint256 internal constant _ROLE_6 = 1 << 6;
            uint256 internal constant _ROLE_7 = 1 << 7;
            uint256 internal constant _ROLE_8 = 1 << 8;
            uint256 internal constant _ROLE_9 = 1 << 9;
            uint256 internal constant _ROLE_10 = 1 << 10;
            uint256 internal constant _ROLE_11 = 1 << 11;
            uint256 internal constant _ROLE_12 = 1 << 12;
            uint256 internal constant _ROLE_13 = 1 << 13;
            uint256 internal constant _ROLE_14 = 1 << 14;
            uint256 internal constant _ROLE_15 = 1 << 15;
            uint256 internal constant _ROLE_16 = 1 << 16;
            uint256 internal constant _ROLE_17 = 1 << 17;
            uint256 internal constant _ROLE_18 = 1 << 18;
            uint256 internal constant _ROLE_19 = 1 << 19;
            uint256 internal constant _ROLE_20 = 1 << 20;
            uint256 internal constant _ROLE_21 = 1 << 21;
            uint256 internal constant _ROLE_22 = 1 << 22;
            uint256 internal constant _ROLE_23 = 1 << 23;
            uint256 internal constant _ROLE_24 = 1 << 24;
            uint256 internal constant _ROLE_25 = 1 << 25;
            uint256 internal constant _ROLE_26 = 1 << 26;
            uint256 internal constant _ROLE_27 = 1 << 27;
            uint256 internal constant _ROLE_28 = 1 << 28;
            uint256 internal constant _ROLE_29 = 1 << 29;
            uint256 internal constant _ROLE_30 = 1 << 30;
            uint256 internal constant _ROLE_31 = 1 << 31;
            uint256 internal constant _ROLE_32 = 1 << 32;
            uint256 internal constant _ROLE_33 = 1 << 33;
            uint256 internal constant _ROLE_34 = 1 << 34;
            uint256 internal constant _ROLE_35 = 1 << 35;
            uint256 internal constant _ROLE_36 = 1 << 36;
            uint256 internal constant _ROLE_37 = 1 << 37;
            uint256 internal constant _ROLE_38 = 1 << 38;
            uint256 internal constant _ROLE_39 = 1 << 39;
            uint256 internal constant _ROLE_40 = 1 << 40;
            uint256 internal constant _ROLE_41 = 1 << 41;
            uint256 internal constant _ROLE_42 = 1 << 42;
            uint256 internal constant _ROLE_43 = 1 << 43;
            uint256 internal constant _ROLE_44 = 1 << 44;
            uint256 internal constant _ROLE_45 = 1 << 45;
            uint256 internal constant _ROLE_46 = 1 << 46;
            uint256 internal constant _ROLE_47 = 1 << 47;
            uint256 internal constant _ROLE_48 = 1 << 48;
            uint256 internal constant _ROLE_49 = 1 << 49;
            uint256 internal constant _ROLE_50 = 1 << 50;
            uint256 internal constant _ROLE_51 = 1 << 51;
            uint256 internal constant _ROLE_52 = 1 << 52;
            uint256 internal constant _ROLE_53 = 1 << 53;
            uint256 internal constant _ROLE_54 = 1 << 54;
            uint256 internal constant _ROLE_55 = 1 << 55;
            uint256 internal constant _ROLE_56 = 1 << 56;
            uint256 internal constant _ROLE_57 = 1 << 57;
            uint256 internal constant _ROLE_58 = 1 << 58;
            uint256 internal constant _ROLE_59 = 1 << 59;
            uint256 internal constant _ROLE_60 = 1 << 60;
            uint256 internal constant _ROLE_61 = 1 << 61;
            uint256 internal constant _ROLE_62 = 1 << 62;
            uint256 internal constant _ROLE_63 = 1 << 63;
            uint256 internal constant _ROLE_64 = 1 << 64;
            uint256 internal constant _ROLE_65 = 1 << 65;
            uint256 internal constant _ROLE_66 = 1 << 66;
            uint256 internal constant _ROLE_67 = 1 << 67;
            uint256 internal constant _ROLE_68 = 1 << 68;
            uint256 internal constant _ROLE_69 = 1 << 69;
            uint256 internal constant _ROLE_70 = 1 << 70;
            uint256 internal constant _ROLE_71 = 1 << 71;
            uint256 internal constant _ROLE_72 = 1 << 72;
            uint256 internal constant _ROLE_73 = 1 << 73;
            uint256 internal constant _ROLE_74 = 1 << 74;
            uint256 internal constant _ROLE_75 = 1 << 75;
            uint256 internal constant _ROLE_76 = 1 << 76;
            uint256 internal constant _ROLE_77 = 1 << 77;
            uint256 internal constant _ROLE_78 = 1 << 78;
            uint256 internal constant _ROLE_79 = 1 << 79;
            uint256 internal constant _ROLE_80 = 1 << 80;
            uint256 internal constant _ROLE_81 = 1 << 81;
            uint256 internal constant _ROLE_82 = 1 << 82;
            uint256 internal constant _ROLE_83 = 1 << 83;
            uint256 internal constant _ROLE_84 = 1 << 84;
            uint256 internal constant _ROLE_85 = 1 << 85;
            uint256 internal constant _ROLE_86 = 1 << 86;
            uint256 internal constant _ROLE_87 = 1 << 87;
            uint256 internal constant _ROLE_88 = 1 << 88;
            uint256 internal constant _ROLE_89 = 1 << 89;
            uint256 internal constant _ROLE_90 = 1 << 90;
            uint256 internal constant _ROLE_91 = 1 << 91;
            uint256 internal constant _ROLE_92 = 1 << 92;
            uint256 internal constant _ROLE_93 = 1 << 93;
            uint256 internal constant _ROLE_94 = 1 << 94;
            uint256 internal constant _ROLE_95 = 1 << 95;
            uint256 internal constant _ROLE_96 = 1 << 96;
            uint256 internal constant _ROLE_97 = 1 << 97;
            uint256 internal constant _ROLE_98 = 1 << 98;
            uint256 internal constant _ROLE_99 = 1 << 99;
            uint256 internal constant _ROLE_100 = 1 << 100;
            uint256 internal constant _ROLE_101 = 1 << 101;
            uint256 internal constant _ROLE_102 = 1 << 102;
            uint256 internal constant _ROLE_103 = 1 << 103;
            uint256 internal constant _ROLE_104 = 1 << 104;
            uint256 internal constant _ROLE_105 = 1 << 105;
            uint256 internal constant _ROLE_106 = 1 << 106;
            uint256 internal constant _ROLE_107 = 1 << 107;
            uint256 internal constant _ROLE_108 = 1 << 108;
            uint256 internal constant _ROLE_109 = 1 << 109;
            uint256 internal constant _ROLE_110 = 1 << 110;
            uint256 internal constant _ROLE_111 = 1 << 111;
            uint256 internal constant _ROLE_112 = 1 << 112;
            uint256 internal constant _ROLE_113 = 1 << 113;
            uint256 internal constant _ROLE_114 = 1 << 114;
            uint256 internal constant _ROLE_115 = 1 << 115;
            uint256 internal constant _ROLE_116 = 1 << 116;
            uint256 internal constant _ROLE_117 = 1 << 117;
            uint256 internal constant _ROLE_118 = 1 << 118;
            uint256 internal constant _ROLE_119 = 1 << 119;
            uint256 internal constant _ROLE_120 = 1 << 120;
            uint256 internal constant _ROLE_121 = 1 << 121;
            uint256 internal constant _ROLE_122 = 1 << 122;
            uint256 internal constant _ROLE_123 = 1 << 123;
            uint256 internal constant _ROLE_124 = 1 << 124;
            uint256 internal constant _ROLE_125 = 1 << 125;
            uint256 internal constant _ROLE_126 = 1 << 126;
            uint256 internal constant _ROLE_127 = 1 << 127;
            uint256 internal constant _ROLE_128 = 1 << 128;
            uint256 internal constant _ROLE_129 = 1 << 129;
            uint256 internal constant _ROLE_130 = 1 << 130;
            uint256 internal constant _ROLE_131 = 1 << 131;
            uint256 internal constant _ROLE_132 = 1 << 132;
            uint256 internal constant _ROLE_133 = 1 << 133;
            uint256 internal constant _ROLE_134 = 1 << 134;
            uint256 internal constant _ROLE_135 = 1 << 135;
            uint256 internal constant _ROLE_136 = 1 << 136;
            uint256 internal constant _ROLE_137 = 1 << 137;
            uint256 internal constant _ROLE_138 = 1 << 138;
            uint256 internal constant _ROLE_139 = 1 << 139;
            uint256 internal constant _ROLE_140 = 1 << 140;
            uint256 internal constant _ROLE_141 = 1 << 141;
            uint256 internal constant _ROLE_142 = 1 << 142;
            uint256 internal constant _ROLE_143 = 1 << 143;
            uint256 internal constant _ROLE_144 = 1 << 144;
            uint256 internal constant _ROLE_145 = 1 << 145;
            uint256 internal constant _ROLE_146 = 1 << 146;
            uint256 internal constant _ROLE_147 = 1 << 147;
            uint256 internal constant _ROLE_148 = 1 << 148;
            uint256 internal constant _ROLE_149 = 1 << 149;
            uint256 internal constant _ROLE_150 = 1 << 150;
            uint256 internal constant _ROLE_151 = 1 << 151;
            uint256 internal constant _ROLE_152 = 1 << 152;
            uint256 internal constant _ROLE_153 = 1 << 153;
            uint256 internal constant _ROLE_154 = 1 << 154;
            uint256 internal constant _ROLE_155 = 1 << 155;
            uint256 internal constant _ROLE_156 = 1 << 156;
            uint256 internal constant _ROLE_157 = 1 << 157;
            uint256 internal constant _ROLE_158 = 1 << 158;
            uint256 internal constant _ROLE_159 = 1 << 159;
            uint256 internal constant _ROLE_160 = 1 << 160;
            uint256 internal constant _ROLE_161 = 1 << 161;
            uint256 internal constant _ROLE_162 = 1 << 162;
            uint256 internal constant _ROLE_163 = 1 << 163;
            uint256 internal constant _ROLE_164 = 1 << 164;
            uint256 internal constant _ROLE_165 = 1 << 165;
            uint256 internal constant _ROLE_166 = 1 << 166;
            uint256 internal constant _ROLE_167 = 1 << 167;
            uint256 internal constant _ROLE_168 = 1 << 168;
            uint256 internal constant _ROLE_169 = 1 << 169;
            uint256 internal constant _ROLE_170 = 1 << 170;
            uint256 internal constant _ROLE_171 = 1 << 171;
            uint256 internal constant _ROLE_172 = 1 << 172;
            uint256 internal constant _ROLE_173 = 1 << 173;
            uint256 internal constant _ROLE_174 = 1 << 174;
            uint256 internal constant _ROLE_175 = 1 << 175;
            uint256 internal constant _ROLE_176 = 1 << 176;
            uint256 internal constant _ROLE_177 = 1 << 177;
            uint256 internal constant _ROLE_178 = 1 << 178;
            uint256 internal constant _ROLE_179 = 1 << 179;
            uint256 internal constant _ROLE_180 = 1 << 180;
            uint256 internal constant _ROLE_181 = 1 << 181;
            uint256 internal constant _ROLE_182 = 1 << 182;
            uint256 internal constant _ROLE_183 = 1 << 183;
            uint256 internal constant _ROLE_184 = 1 << 184;
            uint256 internal constant _ROLE_185 = 1 << 185;
            uint256 internal constant _ROLE_186 = 1 << 186;
            uint256 internal constant _ROLE_187 = 1 << 187;
            uint256 internal constant _ROLE_188 = 1 << 188;
            uint256 internal constant _ROLE_189 = 1 << 189;
            uint256 internal constant _ROLE_190 = 1 << 190;
            uint256 internal constant _ROLE_191 = 1 << 191;
            uint256 internal constant _ROLE_192 = 1 << 192;
            uint256 internal constant _ROLE_193 = 1 << 193;
            uint256 internal constant _ROLE_194 = 1 << 194;
            uint256 internal constant _ROLE_195 = 1 << 195;
            uint256 internal constant _ROLE_196 = 1 << 196;
            uint256 internal constant _ROLE_197 = 1 << 197;
            uint256 internal constant _ROLE_198 = 1 << 198;
            uint256 internal constant _ROLE_199 = 1 << 199;
            uint256 internal constant _ROLE_200 = 1 << 200;
            uint256 internal constant _ROLE_201 = 1 << 201;
            uint256 internal constant _ROLE_202 = 1 << 202;
            uint256 internal constant _ROLE_203 = 1 << 203;
            uint256 internal constant _ROLE_204 = 1 << 204;
            uint256 internal constant _ROLE_205 = 1 << 205;
            uint256 internal constant _ROLE_206 = 1 << 206;
            uint256 internal constant _ROLE_207 = 1 << 207;
            uint256 internal constant _ROLE_208 = 1 << 208;
            uint256 internal constant _ROLE_209 = 1 << 209;
            uint256 internal constant _ROLE_210 = 1 << 210;
            uint256 internal constant _ROLE_211 = 1 << 211;
            uint256 internal constant _ROLE_212 = 1 << 212;
            uint256 internal constant _ROLE_213 = 1 << 213;
            uint256 internal constant _ROLE_214 = 1 << 214;
            uint256 internal constant _ROLE_215 = 1 << 215;
            uint256 internal constant _ROLE_216 = 1 << 216;
            uint256 internal constant _ROLE_217 = 1 << 217;
            uint256 internal constant _ROLE_218 = 1 << 218;
            uint256 internal constant _ROLE_219 = 1 << 219;
            uint256 internal constant _ROLE_220 = 1 << 220;
            uint256 internal constant _ROLE_221 = 1 << 221;
            uint256 internal constant _ROLE_222 = 1 << 222;
            uint256 internal constant _ROLE_223 = 1 << 223;
            uint256 internal constant _ROLE_224 = 1 << 224;
            uint256 internal constant _ROLE_225 = 1 << 225;
            uint256 internal constant _ROLE_226 = 1 << 226;
            uint256 internal constant _ROLE_227 = 1 << 227;
            uint256 internal constant _ROLE_228 = 1 << 228;
            uint256 internal constant _ROLE_229 = 1 << 229;
            uint256 internal constant _ROLE_230 = 1 << 230;
            uint256 internal constant _ROLE_231 = 1 << 231;
            uint256 internal constant _ROLE_232 = 1 << 232;
            uint256 internal constant _ROLE_233 = 1 << 233;
            uint256 internal constant _ROLE_234 = 1 << 234;
            uint256 internal constant _ROLE_235 = 1 << 235;
            uint256 internal constant _ROLE_236 = 1 << 236;
            uint256 internal constant _ROLE_237 = 1 << 237;
            uint256 internal constant _ROLE_238 = 1 << 238;
            uint256 internal constant _ROLE_239 = 1 << 239;
            uint256 internal constant _ROLE_240 = 1 << 240;
            uint256 internal constant _ROLE_241 = 1 << 241;
            uint256 internal constant _ROLE_242 = 1 << 242;
            uint256 internal constant _ROLE_243 = 1 << 243;
            uint256 internal constant _ROLE_244 = 1 << 244;
            uint256 internal constant _ROLE_245 = 1 << 245;
            uint256 internal constant _ROLE_246 = 1 << 246;
            uint256 internal constant _ROLE_247 = 1 << 247;
            uint256 internal constant _ROLE_248 = 1 << 248;
            uint256 internal constant _ROLE_249 = 1 << 249;
            uint256 internal constant _ROLE_250 = 1 << 250;
            uint256 internal constant _ROLE_251 = 1 << 251;
            uint256 internal constant _ROLE_252 = 1 << 252;
            uint256 internal constant _ROLE_253 = 1 << 253;
            uint256 internal constant _ROLE_254 = 1 << 254;
            uint256 internal constant _ROLE_255 = 1 << 255;
        }
        // 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 use signatures as unique identifiers:
        /// - 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.
        /// This implementation does NOT check if a signature is non-malleable.
        library ECDSA {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                        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://eth.wiki/json-rpc/API#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:\
        32") // 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://eth.wiki/json-rpc/API#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:\
        ") // 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.
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   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 Contract for EIP-712 typed structured data hashing and signing.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
        /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
        /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
        ///
        /// @dev Note, this implementation:
        /// - Uses `address(this)` for the `verifyingContract` field.
        /// - Does NOT use the optional EIP-712 salt.
        /// - Does NOT use any EIP-712 extensions.
        /// This is for simplicity and to save gas.
        /// If you need to customize, please fork / modify accordingly.
        abstract contract EIP712 {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  CONSTANTS AND IMMUTABLES                  */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
            bytes32 internal constant _DOMAIN_TYPEHASH =
                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
            uint256 private immutable _cachedThis;
            uint256 private immutable _cachedChainId;
            bytes32 private immutable _cachedNameHash;
            bytes32 private immutable _cachedVersionHash;
            bytes32 private immutable _cachedDomainSeparator;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                        CONSTRUCTOR                         */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Cache the hashes for cheaper runtime gas costs.
            /// In the case of upgradeable contracts (i.e. proxies),
            /// or if the chain id changes due to a hard fork,
            /// the domain separator will be seamlessly calculated on-the-fly.
            constructor() {
                _cachedThis = uint256(uint160(address(this)));
                _cachedChainId = block.chainid;
                string memory name;
                string memory version;
                if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
                bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
                bytes32 versionHash =
                    _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
                _cachedNameHash = nameHash;
                _cachedVersionHash = versionHash;
                bytes32 separator;
                if (!_domainNameAndVersionMayChange()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Load the free memory pointer.
                        mstore(m, _DOMAIN_TYPEHASH)
                        mstore(add(m, 0x20), nameHash)
                        mstore(add(m, 0x40), versionHash)
                        mstore(add(m, 0x60), chainid())
                        mstore(add(m, 0x80), address())
                        separator := keccak256(m, 0xa0)
                    }
                }
                _cachedDomainSeparator = separator;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   FUNCTIONS TO OVERRIDE                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Please override this function to return the domain name and version.
            /// ```
            ///     function _domainNameAndVersion()
            ///         internal
            ///         pure
            ///         virtual
            ///         returns (string memory name, string memory version)
            ///     {
            ///         name = "Solady";
            ///         version = "1";
            ///     }
            /// ```
            ///
            /// Note: If the returned result may change after the contract has been deployed,
            /// you must override `_domainNameAndVersionMayChange()` to return true.
            function _domainNameAndVersion()
                internal
                view
                virtual
                returns (string memory name, string memory version);
            /// @dev Returns if `_domainNameAndVersion()` may change
            /// after the contract has been deployed (i.e. after the constructor).
            /// Default: false.
            function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     HASHING OPERATIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the EIP-712 domain separator.
            function _domainSeparator() internal view virtual returns (bytes32 separator) {
                if (_domainNameAndVersionMayChange()) {
                    separator = _buildDomainSeparator();
                } else {
                    separator = _cachedDomainSeparator;
                    if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
                }
            }
            /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
            /// given `structHash`, as defined in
            /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
            ///
            /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
            /// ```
            ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
            ///         keccak256("Mail(address to,string contents)"),
            ///         mailTo,
            ///         keccak256(bytes(mailContents))
            ///     )));
            ///     address signer = ECDSA.recover(digest, signature);
            /// ```
            function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
                // We will use `digest` to store the domain separator to save a bit of gas.
                if (_domainNameAndVersionMayChange()) {
                    digest = _buildDomainSeparator();
                } else {
                    digest = _cachedDomainSeparator;
                    if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
                }
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the digest.
                    mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                    mstore(0x1a, digest) // Store the domain separator.
                    mstore(0x3a, structHash) // Store the struct hash.
                    digest := keccak256(0x18, 0x42)
                    // Restore the part of the free memory slot that was overwritten.
                    mstore(0x3a, 0)
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                    EIP-5267 OPERATIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
            function eip712Domain()
                public
                view
                virtual
                returns (
                    bytes1 fields,
                    string memory name,
                    string memory version,
                    uint256 chainId,
                    address verifyingContract,
                    bytes32 salt,
                    uint256[] memory extensions
                )
            {
                fields = hex"0f"; // `0b01111`.
                (name, version) = _domainNameAndVersion();
                chainId = block.chainid;
                verifyingContract = address(this);
                salt = salt; // `bytes32(0)`.
                extensions = extensions; // `new uint256[](0)`.
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      PRIVATE HELPERS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the EIP-712 domain separator.
            function _buildDomainSeparator() private view returns (bytes32 separator) {
                // We will use `separator` to store the name hash to save a bit of gas.
                bytes32 versionHash;
                if (_domainNameAndVersionMayChange()) {
                    (string memory name, string memory version) = _domainNameAndVersion();
                    separator = keccak256(bytes(name));
                    versionHash = keccak256(bytes(version));
                } else {
                    separator = _cachedNameHash;
                    versionHash = _cachedVersionHash;
                }
                /// @solidity memory-safe-assembly
                assembly {
                    let m := mload(0x40) // Load the free memory pointer.
                    mstore(m, _DOMAIN_TYPEHASH)
                    mstore(add(m, 0x20), separator) // Name hash.
                    mstore(add(m, 0x40), versionHash)
                    mstore(add(m, 0x60), chainid())
                    mstore(add(m, 0x80), address())
                    separator := keccak256(m, 0xa0)
                }
            }
            /// @dev Returns if the cached domain separator has been invalidated.
            function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
                uint256 cachedChainId = _cachedChainId;
                uint256 cachedThis = _cachedThis;
                /// @solidity memory-safe-assembly
                assembly {
                    result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Library for managing enumerable sets in storage.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibMap.sol)
        ///
        /// @dev Note:
        /// In many applications, the number of elements in an enumerable set is small.
        /// This enumerable set implementation avoids storing the length and indices
        /// for up to 3 elements. Once the length exceeds 3 for the first time, the length
        /// and indices will be initialized. The amortized cost of adding elements is O(1).
        ///
        /// The AddressSet implementation packs the length with the 0th entry.
        library EnumerableSetLib {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The index must be less than the length.
            error IndexOutOfBounds();
            /// @dev The value cannot be the zero sentinel.
            error ValueIsZeroSentinel();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         CONSTANTS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev A sentinel value to denote the zero value in storage.
            /// No elements can be equal to this value.
            /// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
            uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;
            /// @dev The storage layout is given by:
            /// ```
            ///     mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
            ///     mstore(0x00, set.slot)
            ///     let rootSlot := keccak256(0x00, 0x24)
            ///     mstore(0x20, rootSlot)
            ///     mstore(0x00, shr(96, shl(96, value)))
            ///     let positionSlot := keccak256(0x00, 0x40)
            ///     let valueSlot := add(rootSlot, sload(positionSlot))
            ///     let valueInStorage := shr(96, sload(valueSlot))
            ///     let lazyLength := shr(160, shl(160, sload(rootSlot)))
            /// ```
            uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;
            /// @dev The storage layout is given by:
            /// ```
            ///     mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
            ///     mstore(0x00, set.slot)
            ///     let rootSlot := keccak256(0x00, 0x24)
            ///     mstore(0x20, rootSlot)
            ///     mstore(0x00, value)
            ///     let positionSlot := keccak256(0x00, 0x40)
            ///     let valueSlot := add(rootSlot, sload(positionSlot))
            ///     let valueInStorage := sload(valueSlot)
            ///     let lazyLength := sload(not(rootSlot))
            /// ```
            uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STRUCTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev An enumerable address set in storage.
            struct AddressSet {
                uint256 _spacer;
            }
            /// @dev An enumerable bytes32 set in storage.
            struct Bytes32Set {
                uint256 _spacer;
            }
            /// @dev An enumerable uint256 set in storage.
            struct Uint256Set {
                uint256 _spacer;
            }
            /// @dev An enumerable int256 set in storage.
            struct Int256Set {
                uint256 _spacer;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     GETTERS / SETTERS                      */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the number of elements in the set.
            function length(AddressSet storage set) internal view returns (uint256 result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let rootPacked := sload(rootSlot)
                    let n := shr(160, shl(160, rootPacked))
                    result := shr(1, n)
                    for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
                        result := 1
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 2
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 3
                        break
                    }
                }
            }
            /// @dev Returns the number of elements in the set.
            function length(Bytes32Set storage set) internal view returns (uint256 result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let n := sload(not(rootSlot))
                    result := shr(1, n)
                    for {} iszero(n) {} {
                        result := 0
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 1
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 2
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 3
                        break
                    }
                }
            }
            /// @dev Returns the number of elements in the set.
            function length(Uint256Set storage set) internal view returns (uint256 result) {
                result = length(_toBytes32Set(set));
            }
            /// @dev Returns the number of elements in the set.
            function length(Int256Set storage set) internal view returns (uint256 result) {
                result = length(_toBytes32Set(set));
            }
            /// @dev Returns whether `value` is in the set.
            function contains(AddressSet storage set, address value) internal view returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for {} 1 {} {
                        if iszero(shr(160, shl(160, rootPacked))) {
                            result := 1
                            if eq(shr(96, rootPacked), value) { break }
                            if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
                            if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                        break
                    }
                }
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for {} 1 {} {
                        if iszero(sload(not(rootSlot))) {
                            result := 1
                            if eq(sload(rootSlot), value) { break }
                            if eq(sload(add(rootSlot, 1)), value) { break }
                            if eq(sload(add(rootSlot, 2)), value) { break }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                        break
                    }
                }
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
                result = contains(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
                result = contains(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(AddressSet storage set, address value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                        mstore(0x20, rootSlot)
                        if iszero(n) {
                            let v0 := shr(96, rootPacked)
                            if iszero(v0) {
                                sstore(rootSlot, shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v0, value) { break }
                            let v1 := shr(96, sload(add(rootSlot, 1)))
                            if iszero(v1) {
                                sstore(add(rootSlot, 1), shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v1, value) { break }
                            let v2 := shr(96, sload(add(rootSlot, 2)))
                            if iszero(v2) {
                                sstore(add(rootSlot, 2), shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v2, value) { break }
                            mstore(0x00, v0)
                            sstore(keccak256(0x00, 0x40), 1)
                            mstore(0x00, v1)
                            sstore(keccak256(0x00, 0x40), 2)
                            mstore(0x00, v2)
                            sstore(keccak256(0x00, 0x40), 3)
                            rootPacked := or(rootPacked, 7)
                            n := 7
                        }
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        if iszero(sload(p)) {
                            n := shr(1, n)
                            sstore(add(rootSlot, n), shl(96, value))
                            sstore(p, add(1, n))
                            sstore(rootSlot, add(2, rootPacked))
                            result := 1
                            break
                        }
                        break
                    }
                }
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for { let n := sload(not(rootSlot)) } 1 {} {
                        mstore(0x20, rootSlot)
                        if iszero(n) {
                            let v0 := sload(rootSlot)
                            if iszero(v0) {
                                sstore(rootSlot, value)
                                result := 1
                                break
                            }
                            if eq(v0, value) { break }
                            let v1 := sload(add(rootSlot, 1))
                            if iszero(v1) {
                                sstore(add(rootSlot, 1), value)
                                result := 1
                                break
                            }
                            if eq(v1, value) { break }
                            let v2 := sload(add(rootSlot, 2))
                            if iszero(v2) {
                                sstore(add(rootSlot, 2), value)
                                result := 1
                                break
                            }
                            if eq(v2, value) { break }
                            mstore(0x00, v0)
                            sstore(keccak256(0x00, 0x40), 1)
                            mstore(0x00, v1)
                            sstore(keccak256(0x00, 0x40), 2)
                            mstore(0x00, v2)
                            sstore(keccak256(0x00, 0x40), 3)
                            n := 7
                        }
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        if iszero(sload(p)) {
                            n := shr(1, n)
                            sstore(add(rootSlot, n), value)
                            sstore(p, add(1, n))
                            sstore(not(rootSlot), or(1, shl(1, add(1, n))))
                            result := 1
                            break
                        }
                        break
                    }
                }
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
                result = add(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Int256Set storage set, int256 value) internal returns (bool result) {
                result = add(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(AddressSet storage set, address value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                        if iszero(n) {
                            result := 1
                            if eq(shr(96, rootPacked), value) {
                                sstore(rootSlot, sload(add(rootSlot, 1)))
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(shr(96, sload(add(rootSlot, 1))), value) {
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(shr(96, sload(add(rootSlot, 2))), value) {
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        let position := sload(p)
                        if iszero(position) { break }
                        n := sub(shr(1, n), 1)
                        if iszero(eq(sub(position, 1), n)) {
                            let lastValue := shr(96, sload(add(rootSlot, n)))
                            sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
                            sstore(add(rootSlot, n), 0)
                            mstore(0x00, lastValue)
                            sstore(keccak256(0x00, 0x40), position)
                        }
                        sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
                        sstore(p, 0)
                        result := 1
                        break
                    }
                }
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for { let n := sload(not(rootSlot)) } 1 {} {
                        if iszero(n) {
                            result := 1
                            if eq(sload(rootSlot), value) {
                                sstore(rootSlot, sload(add(rootSlot, 1)))
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(sload(add(rootSlot, 1)), value) {
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(sload(add(rootSlot, 2)), value) {
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        let position := sload(p)
                        if iszero(position) { break }
                        n := sub(shr(1, n), 1)
                        if iszero(eq(sub(position, 1), n)) {
                            let lastValue := sload(add(rootSlot, n))
                            sstore(add(rootSlot, sub(position, 1)), lastValue)
                            sstore(add(rootSlot, n), 0)
                            mstore(0x00, lastValue)
                            sstore(keccak256(0x00, 0x40), position)
                        }
                        sstore(not(rootSlot), or(shl(1, n), 1))
                        sstore(p, 0)
                        result := 1
                        break
                    }
                }
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
                result = remove(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Int256Set storage set, int256 value) internal returns (bool result) {
                result = remove(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(AddressSet storage set) internal view returns (address[] memory result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let zs := _ZERO_SENTINEL
                    let rootPacked := sload(rootSlot)
                    let n := shr(160, shl(160, rootPacked))
                    result := mload(0x40)
                    let o := add(0x20, result)
                    let v := shr(96, rootPacked)
                    mstore(o, mul(v, iszero(eq(v, zs))))
                    for {} 1 {} {
                        if iszero(n) {
                            if v {
                                n := 1
                                v := shr(96, sload(add(rootSlot, n)))
                                if v {
                                    n := 2
                                    mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                                    v := shr(96, sload(add(rootSlot, n)))
                                    if v {
                                        n := 3
                                        mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                                    }
                                }
                            }
                            break
                        }
                        n := shr(1, n)
                        for { let i := 1 } lt(i, n) { i := add(i, 1) } {
                            v := shr(96, sload(add(rootSlot, i)))
                            mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                        }
                        break
                    }
                    mstore(result, n)
                    mstore(0x40, add(o, shl(5, n)))
                }
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let zs := _ZERO_SENTINEL
                    let n := sload(not(rootSlot))
                    result := mload(0x40)
                    let o := add(0x20, result)
                    for {} 1 {} {
                        if iszero(n) {
                            let v := sload(rootSlot)
                            if v {
                                n := 1
                                mstore(o, mul(v, iszero(eq(v, zs))))
                                v := sload(add(rootSlot, n))
                                if v {
                                    n := 2
                                    mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                                    v := sload(add(rootSlot, n))
                                    if v {
                                        n := 3
                                        mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                                    }
                                }
                            }
                            break
                        }
                        n := shr(1, n)
                        for { let i := 0 } lt(i, n) { i := add(i, 1) } {
                            let v := sload(add(rootSlot, i))
                            mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                        }
                        break
                    }
                    mstore(result, n)
                    mstore(0x40, add(o, shl(5, n)))
                }
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
                result = _toUints(values(_toBytes32Set(set)));
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Int256Set storage set) internal view returns (int256[] memory result) {
                result = _toInts(values(_toBytes32Set(set)));
            }
            /// @dev Returns the element at index `i` in the set.
            function at(AddressSet storage set, uint256 i) internal view returns (address result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    result := shr(96, sload(add(rootSlot, i)))
                    result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
                }
                if (i >= length(set)) revert IndexOutOfBounds();
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
                result = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    result := sload(add(result, i))
                    result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
                }
                if (i >= length(set)) revert IndexOutOfBounds();
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
                result = uint256(at(_toBytes32Set(set), i));
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
                result = int256(uint256(at(_toBytes32Set(set), i)));
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      PRIVATE HELPERS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the root slot.
            function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
                    mstore(0x00, s.slot)
                    r := keccak256(0x00, 0x24)
                }
            }
            /// @dev Returns the root slot.
            function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
                    mstore(0x00, s.slot)
                    r := keccak256(0x00, 0x24)
                }
            }
            /// @dev Casts to a Bytes32Set.
            function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c.slot := s.slot
                }
            }
            /// @dev Casts to a Bytes32Set.
            function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c.slot := s.slot
                }
            }
            /// @dev Casts to a uint256 array.
            function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c := a
                }
            }
            /// @dev Casts to a int256 array.
            function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c := a
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Initializable mixin for the upgradeable contracts.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
        /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
        abstract contract Initializable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The contract is already initialized.
            error InvalidInitialization();
            /// @dev The contract is not initializing.
            error NotInitializing();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Triggered when the contract has been initialized.
            event Initialized(uint64 version);
            /// @dev `keccak256(bytes("Initialized(uint64)"))`.
            bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
                0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The default initializable slot is given by:
            /// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
            ///
            /// Bits Layout:
            /// - [0]     `initializing`
            /// - [1..64] `initializedVersion`
            bytes32 private constant _INITIALIZABLE_SLOT =
                0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         OPERATIONS                         */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Override to return a custom storage slot if required.
            function _initializableSlot() internal pure virtual returns (bytes32) {
                return _INITIALIZABLE_SLOT;
            }
            /// @dev Guards an initializer function so that it can be invoked at most once.
            ///
            /// You can guard a function with `onlyInitializing` such that it can be called
            /// through a function guarded with `initializer`.
            ///
            /// This is similar to `reinitializer(1)`, except that in the context of a constructor,
            /// an `initializer` guarded function can be invoked multiple times.
            /// This can be useful during testing and is not expected to be used in production.
            ///
            /// Emits an {Initialized} event.
            modifier initializer() virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    let i := sload(s)
                    // Set `initializing` to 1, `initializedVersion` to 1.
                    sstore(s, 3)
                    // If `!(initializing == 0 && initializedVersion == 0)`.
                    if i {
                        // If `!(address(this).code.length == 0 && initializedVersion == 1)`.
                        if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
                            mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                            revert(0x1c, 0x04)
                        }
                        s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
                    }
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    if s {
                        // Set `initializing` to 0, `initializedVersion` to 1.
                        sstore(s, 2)
                        // Emit the {Initialized} event.
                        mstore(0x20, 1)
                        log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                    }
                }
            }
            /// @dev Guards an reinitialzer function so that it can be invoked at most once.
            ///
            /// You can guard a function with `onlyInitializing` such that it can be called
            /// through a function guarded with `reinitializer`.
            ///
            /// Emits an {Initialized} event.
            modifier reinitializer(uint64 version) virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    version := and(version, 0xffffffffffffffff) // Clean upper bits.
                    let i := sload(s)
                    // If `initializing == 1 || initializedVersion >= version`.
                    if iszero(lt(and(i, 1), lt(shr(1, i), version))) {
                        mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                        revert(0x1c, 0x04)
                    }
                    // Set `initializing` to 1, `initializedVersion` to `version`.
                    sstore(s, or(1, shl(1, version)))
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    // Set `initializing` to 0, `initializedVersion` to `version`.
                    sstore(s, shl(1, version))
                    // Emit the {Initialized} event.
                    mstore(0x20, version)
                    log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                }
            }
            /// @dev Guards a function such that it can only be called in the scope
            /// of a function guarded with `initializer` or `reinitializer`.
            modifier onlyInitializing() virtual {
                _checkInitializing();
                _;
            }
            /// @dev Reverts if the contract is not initializing.
            function _checkInitializing() internal view virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    if iszero(and(1, sload(s))) {
                        mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
            ///
            /// Calling this in the constructor will prevent the contract from being initialized
            /// or reinitialized. 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 called.
            function _disableInitializers() internal virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    let i := sload(s)
                    if and(i, 1) {
                        mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                        revert(0x1c, 0x04)
                    }
                    let uint64max := shr(192, s) // Computed to save bytecode.
                    if iszero(eq(shr(1, i), uint64max)) {
                        // Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
                        sstore(s, shl(1, uint64max))
                        // Emit the {Initialized} event.
                        mstore(0x20, uint64max)
                        log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                    }
                }
            }
            /// @dev Returns the highest version that has been initialized.
            function _getInitializedVersion() internal view virtual returns (uint64 version) {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    version := shr(1, sload(s))
                }
            }
            /// @dev Returns whether the contract is currently initializing.
            function _isInitializing() internal view virtual returns (bool result) {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    result := and(1, sload(s))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Contract that enables a single call to call multiple methods on itself.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol)
        /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol)
        ///
        /// WARNING:
        /// This implementation is NOT to be used with ERC2771 out-of-the-box.
        /// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
        /// This also applies to potentially other ERCs / patterns appending to the back of calldata.
        ///
        /// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context.
        /// Moreover, it is infeasible and inefficient for us to add checks and mitigations
        /// for all possible ERC / patterns appending to the back of calldata.
        ///
        /// We would highly recommend using an alternative pattern such as
        /// https://github.com/Vectorized/multicaller
        /// which is more flexible, futureproof, and safer by default.
        abstract contract Multicallable {
            /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`,
            /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`.
            /// If any of the `DELEGATECALL`s reverts, the entire context is reverted,
            /// and the error is bubbled up.
            ///
            /// This function is deliberately made non-payable to guard against double-spending.
            /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong)
            ///
            /// For efficiency, this function will directly return the results, terminating the context.
            /// If called internally, it must be called at the end of a function
            /// that returns `(bytes[] memory)`.
            function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) {
                assembly {
                    mstore(0x00, 0x20)
                    mstore(0x20, data.length) // Store `data.length` into `results`.
                    // Early return if no data.
                    if iszero(data.length) { return(0x00, 0x40) }
                    let results := 0x40
                    // `shl` 5 is equivalent to multiplying by 0x20.
                    let end := shl(5, data.length)
                    // Copy the offsets from calldata into memory.
                    calldatacopy(0x40, data.offset, end)
                    // Offset into `results`.
                    let resultsOffset := end
                    // Pointer to the end of `results`.
                    end := add(results, end)
                    for {} 1 {} {
                        // The offset of the current bytes in the calldata.
                        let o := add(data.offset, mload(results))
                        let m := add(resultsOffset, 0x40)
                        // Copy the current bytes from calldata to the memory.
                        calldatacopy(
                            m,
                            add(o, 0x20), // The offset of the current bytes' bytes.
                            calldataload(o) // The length of the current bytes.
                        )
                        if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) {
                            // Bubble up the revert if the delegatecall reverts.
                            returndatacopy(0x00, 0x00, returndatasize())
                            revert(0x00, returndatasize())
                        }
                        // Append the current `resultsOffset` into `results`.
                        mstore(results, resultsOffset)
                        results := add(results, 0x20)
                        // Append the `returndatasize()`, and the return data.
                        mstore(m, returndatasize())
                        returndatacopy(add(m, 0x20), 0x00, returndatasize())
                        // Advance the `resultsOffset` by `returndatasize() + 0x20`,
                        // rounded up to the next multiple of 32.
                        resultsOffset :=
                            and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0)
                        if iszero(lt(results, end)) { break }
                    }
                    return(0x00, add(resultsOffset, 0x40))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Reentrancy guard mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
        abstract contract ReentrancyGuard {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Unauthorized reentrant call.
            error Reentrancy();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
            /// 9 bytes is large enough to avoid collisions with lower slots,
            /// but not too large to result in excessive bytecode bloat.
            uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      REENTRANCY GUARD                      */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Guards a function from reentrancy.
            modifier nonReentrant() virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                        mstore(0x00, 0xab143c06) // `Reentrancy()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(_REENTRANCY_GUARD_SLOT, address())
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    sstore(_REENTRANCY_GUARD_SLOT, codesize())
                }
            }
            /// @dev Guards a view function from read-only reentrancy.
            modifier nonReadReentrant() virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                        mstore(0x00, 0xab143c06) // `Reentrancy()`.
                        revert(0x1c, 0x04)
                    }
                }
                _;
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        // Interface
        import {ICore} from "./interface/ICore.sol";
        import {IInstallationCallback} from "./interface/IInstallationCallback.sol";
        import {IModule} from "./interface/IModule.sol";
        // Utils
        import {Role} from "./Role.sol";
        import {OwnableRoles} from "@solady/auth/OwnableRoles.sol";
        import {EnumerableSetLib} from "@solady/utils/EnumerableSetLib.sol";
        import {ReentrancyGuard} from "@solady/utils/ReentrancyGuard.sol";
        abstract contract Core is ICore, OwnableRoles, ReentrancyGuard {
            using EnumerableSetLib for EnumerableSetLib.AddressSet;
            /*//////////////////////////////////////////////////////////////
                                        TYPES
            //////////////////////////////////////////////////////////////*/
            /// @dev The type of function callable on module contracts.
            enum FunctionType {
                CALLBACK,
                FALLBACK
            }
            /// @dev Internal representation of a fallback function callable via fallback().
            struct InstalledFunction {
                address implementation;
                uint256 permissionBits;
                FunctionType fnType;
            }
            /*//////////////////////////////////////////////////////////////
                                        EVENTS
            //////////////////////////////////////////////////////////////*/
            /// @notice Emitted when a module is installed.
            event ModuleInstalled(address caller, address implementation, address installedModule);
            /// @notice Emitted when a module is uninstalled.
            event ModuleUninstalled(address caller, address implementation, address installedModule);
            /*//////////////////////////////////////////////////////////////
                                        STORAGE
            //////////////////////////////////////////////////////////////*/
            /// @dev The set of addresses of installed modules.
            EnumerableSetLib.AddressSet private modules;
            /// @dev interface ID => counter of modules supporting the interface.
            mapping(bytes4 => uint256) private supportedInterfaceRefCounter;
            /// @dev function selector => function data.
            mapping(bytes4 => InstalledFunction) private functionData_;
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error ModuleOutOfSync();
            error ModuleNotInstalled();
            error ModuleAlreadyInstalled();
            error CallbackFunctionRequired();
            error CallbackExecutionReverted();
            error CallbackFunctionNotSupported();
            error CallbackFunctionAlreadyInstalled();
            error CallbackFunctionUnauthorizedCall();
            error FallbackFunctionAlreadyInstalled();
            error FallbackFunctionNotInstalled();
            error ModuleInterfaceNotCompatible(bytes4 requiredInterfaceId);
            /*//////////////////////////////////////////////////////////////
                                    FALLBACK FUNCTION
            //////////////////////////////////////////////////////////////*/
            /// @notice Routes a call to the appropriate module contract.
            fallback() external payable {
                // Get module function data.
                InstalledFunction memory fn = functionData_[msg.sig];
                // Check: module function data exists.
                if (fn.implementation == address(0)) {
                    revert FallbackFunctionNotInstalled();
                }
                // Check: authorized to call permissioned module function
                if (fn.fnType == FunctionType.CALLBACK) {
                    if (msg.sender != address(this)) {
                        revert CallbackFunctionUnauthorizedCall();
                    }
                } else if (fn.fnType == FunctionType.FALLBACK && fn.permissionBits > 0) {
                    _checkOwnerOrRoles(fn.permissionBits);
                }
                _delegateAndReturn(fn.implementation);
            }
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Returns the list of all callback functions called on some module contract.
            function getSupportedCallbackFunctions() public pure virtual returns (SupportedCallbackFunction[] memory);
            /// @notice Returns a list of addresess and respective module configs of all installed modules.
            function getInstalledModules() external view returns (InstalledModule[] memory _installedModules) {
                uint256 totalInstalled = modules.length();
                _installedModules = new InstalledModule[](totalInstalled);
                for (uint256 i = 0; i < totalInstalled; i++) {
                    address implementation = modules.at(i);
                    _installedModules[i] =
                        InstalledModule({implementation: implementation, config: IModule(implementation).getModuleConfig()});
                }
            }
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Installs a module contract.
            function installModule(address _module, bytes calldata _data)
                external
                payable
                onlyOwnerOrRoles(Role._INSTALLER_ROLE)
            {
                // Install module.
                _installModule(_module, _data);
            }
            /// @notice Uninstalls a module contract.
            function uninstallModule(address _module, bytes calldata _data)
                external
                payable
                onlyOwnerOrRoles(Role._INSTALLER_ROLE)
            {
                // Uninstall module.
                _uninstallModule(_module, _data);
            }
            /// @notice Returns whether a given interface is implemented by the contract.
            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                if (interfaceId == 0xffffffff) {
                    return false;
                }
                if (interfaceId == 0x01ffc9a7) {
                    // ERC165 Interface ID for ERC165
                    return true;
                }
                if (supportedInterfaceRefCounter[interfaceId] > 0) {
                    return true;
                }
                return false;
            }
            /*//////////////////////////////////////////////////////////////
                                    INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Returns whether a given interface is implemented by the contract.
            function _supportsInterfaceViaModules(bytes4 interfaceId) internal view virtual returns (bool) {
                if (interfaceId == 0xffffffff) {
                    return false;
                }
                if (supportedInterfaceRefCounter[interfaceId] > 0) {
                    return true;
                }
                return false;
            }
            /// @dev Installs a module contract.
            function _installModule(address _module, bytes memory _data) internal {
                if (!modules.add(_module)) {
                    revert ModuleAlreadyInstalled();
                }
                // Get module config.
                ModuleConfig memory config = IModule(_module).getModuleConfig();
                // Check: Core supports interface required by module.
                if (config.requiredInterfaces.length != 0) {
                    for (uint256 i = 0; i < config.requiredInterfaces.length; i++) {
                        if (!supportsInterface(config.requiredInterfaces[i])) {
                            revert ModuleInterfaceNotCompatible(config.requiredInterfaces[i]);
                        }
                    }
                }
                // Store interface support inherited via module installation.
                uint256 supportedInterfaceLength = config.supportedInterfaces.length;
                for (uint256 i = 0; i < supportedInterfaceLength; i++) {
                    supportedInterfaceRefCounter[config.supportedInterfaces[i]] += 1;
                }
                // Store callback function data. Only install supported callback functions
                SupportedCallbackFunction[] memory supportedCallbacks = getSupportedCallbackFunctions();
                uint256 supportedCallbacksLength = supportedCallbacks.length;
                uint256 callbackLength = config.callbackFunctions.length;
                for (uint256 i = 0; i < callbackLength; i++) {
                    CallbackFunction memory callbackFunction = config.callbackFunctions[i];
                    // Check: callback function data not already stored.
                    if (functionData_[callbackFunction.selector].implementation != address(0)) {
                        revert CallbackFunctionAlreadyInstalled();
                    }
                    // Check: callback function is supported
                    bool supported = false;
                    for (uint256 j = 0; j < supportedCallbacksLength; j++) {
                        if (supportedCallbacks[j].selector == callbackFunction.selector) {
                            supported = true;
                            break;
                        }
                    }
                    if (!supported) {
                        revert CallbackFunctionNotSupported();
                    }
                    functionData_[callbackFunction.selector] =
                        InstalledFunction({implementation: _module, permissionBits: 0, fnType: FunctionType.CALLBACK});
                }
                // Store module function data.
                uint256 functionLength = config.fallbackFunctions.length;
                for (uint256 i = 0; i < functionLength; i++) {
                    FallbackFunction memory ext = config.fallbackFunctions[i];
                    // Check: module function data not already stored.
                    if (functionData_[ext.selector].implementation != address(0)) {
                        revert FallbackFunctionAlreadyInstalled();
                    }
                    functionData_[ext.selector] = InstalledFunction({
                        implementation: _module,
                        permissionBits: ext.permissionBits,
                        fnType: FunctionType.FALLBACK
                    });
                }
                // Call `onInstall` callback function if module has registered installation callback.
                if (config.registerInstallationCallback) {
                    (bool success, bytes memory returndata) =
                        _module.delegatecall(abi.encodeCall(IInstallationCallback.onInstall, (_data)));
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                }
                emit ModuleInstalled(msg.sender, _module, _module);
            }
            /// @notice Uninstalls a module contract.
            function _uninstallModule(address _module, bytes memory _data) internal {
                // Check: remove and check if the module is installed
                if (!modules.remove(_module)) {
                    revert ModuleNotInstalled();
                }
                // Get module config.
                ModuleConfig memory config = IModule(_module).getModuleConfig();
                uint256 supportedInterfaceLength = config.supportedInterfaces.length;
                for (uint256 i = 0; i < supportedInterfaceLength; i++) {
                    // Note: This should not underflow because module needs to be installed before uninstalling. getModuleConfig should returns the same value during installation and uninstallation.
                    supportedInterfaceRefCounter[config.supportedInterfaces[i]] -= 1;
                }
                // Remove module function data
                uint256 functionLength = config.fallbackFunctions.length;
                for (uint256 i = 0; i < functionLength; i++) {
                    delete functionData_[config.fallbackFunctions[i].selector];
                }
                // Remove callback function data
                uint256 callbackLength = config.callbackFunctions.length;
                for (uint256 i = 0; i < callbackLength; i++) {
                    delete functionData_[config.callbackFunctions[i].selector];
                }
                if (config.registerInstallationCallback) {
                    _module.delegatecall(abi.encodeCall(IInstallationCallback.onUninstall, (_data)));
                }
                emit ModuleUninstalled(msg.sender, _module, _module);
            }
            /// @dev Calls a module callback function and checks whether it is optional or required.
            function _executeCallbackFunction(bytes4 _selector, bytes memory _abiEncodedCalldata)
                internal
                nonReentrant
                returns (bool success, bytes memory returndata)
            {
                InstalledFunction memory callbackFunction = functionData_[_selector];
                // Verify that the function is a callback function
                if (callbackFunction.fnType != FunctionType.CALLBACK) {
                    revert CallbackFunctionNotSupported();
                }
                if (callbackFunction.implementation != address(0)) {
                    (success, returndata) = callbackFunction.implementation.delegatecall(_abiEncodedCalldata);
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                } else {
                    // Get callback mode -- required or not required.
                    SupportedCallbackFunction[] memory functions = getSupportedCallbackFunctions();
                    uint256 len = functions.length;
                    for (uint256 i = 0; i < len; i++) {
                        if (functions[i].selector == _selector) {
                            if (functions[i].mode == CallbackMode.REQUIRED) {
                                revert CallbackFunctionRequired();
                            }
                            break;
                        }
                    }
                }
            }
            /// @dev Calls a module callback function and checks whether it is optional or required.
            function _executeCallbackFunctionView(bytes4 _selector, bytes memory _abiEncodedCalldata)
                internal
                view
                returns (bool success, bytes memory returndata)
            {
                InstalledFunction memory callbackFunction = functionData_[_selector];
                // Verify that the function is a callback function
                if (callbackFunction.fnType != FunctionType.CALLBACK) {
                    revert CallbackFunctionNotSupported();
                }
                if (callbackFunction.implementation != address(0)) {
                    (success, returndata) = address(this).staticcall(_abiEncodedCalldata);
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                } else {
                    // Get callback mode -- required or not required.
                    SupportedCallbackFunction[] memory functions = getSupportedCallbackFunctions();
                    uint256 len = functions.length;
                    for (uint256 i = 0; i < len; i++) {
                        if (functions[i].selector == _selector) {
                            if (functions[i].mode == CallbackMode.REQUIRED) {
                                revert CallbackFunctionRequired();
                            }
                            break;
                        }
                    }
                }
            }
            /// @dev delegateCalls an `implementation` smart contract.
            /// @notice Only use this at the end of the function as it reverts or returns the result
            function _delegateAndReturn(address _implementation) private {
                /// @solidity memory-safe-assembly
                assembly {
                    function allocate(length) -> pos {
                        pos := mload(0x40)
                        mstore(0x40, add(pos, length))
                    }
                    let calldataPtr := allocate(calldatasize())
                    calldatacopy(calldataPtr, 0, calldatasize())
                    let success := delegatecall(gas(), _implementation, calldataPtr, calldatasize(), 0, 0)
                    let returnDataPtr := allocate(returndatasize())
                    returndatacopy(returnDataPtr, 0, returndatasize())
                    if iszero(success) { revert(returnDataPtr, returndatasize()) }
                    return(returnDataPtr, returndatasize())
                }
            }
            /// @dev Reverts with the given return data / error message.
            function _revert(bytes memory _returnData, bytes4 _errorSignature) internal 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 {
                        revert(add(0x20, _returnData), mload(_returnData))
                    }
                } else {
                    assembly {
                        mstore(0x00, _errorSignature)
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        library Role {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   NAMED ROLE CONSTANTS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            uint256 internal constant _MINTER_ROLE = 1 << 0;
            uint256 internal constant _MANAGER_ROLE = 1 << 1;
            uint256 internal constant _INSTALLER_ROLE = 1 << 255;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       ROLE CONSTANTS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            uint256 internal constant _ROLE_0 = 1 << 0;
            uint256 internal constant _ROLE_1 = 1 << 1;
            uint256 internal constant _ROLE_2 = 1 << 2;
            uint256 internal constant _ROLE_3 = 1 << 3;
            uint256 internal constant _ROLE_4 = 1 << 4;
            uint256 internal constant _ROLE_5 = 1 << 5;
            uint256 internal constant _ROLE_6 = 1 << 6;
            uint256 internal constant _ROLE_7 = 1 << 7;
            uint256 internal constant _ROLE_8 = 1 << 8;
            uint256 internal constant _ROLE_9 = 1 << 9;
            uint256 internal constant _ROLE_10 = 1 << 10;
            uint256 internal constant _ROLE_11 = 1 << 11;
            uint256 internal constant _ROLE_12 = 1 << 12;
            uint256 internal constant _ROLE_13 = 1 << 13;
            uint256 internal constant _ROLE_14 = 1 << 14;
            uint256 internal constant _ROLE_15 = 1 << 15;
            uint256 internal constant _ROLE_16 = 1 << 16;
            uint256 internal constant _ROLE_17 = 1 << 17;
            uint256 internal constant _ROLE_18 = 1 << 18;
            uint256 internal constant _ROLE_19 = 1 << 19;
            uint256 internal constant _ROLE_20 = 1 << 20;
            uint256 internal constant _ROLE_21 = 1 << 21;
            uint256 internal constant _ROLE_22 = 1 << 22;
            uint256 internal constant _ROLE_23 = 1 << 23;
            uint256 internal constant _ROLE_24 = 1 << 24;
            uint256 internal constant _ROLE_25 = 1 << 25;
            uint256 internal constant _ROLE_26 = 1 << 26;
            uint256 internal constant _ROLE_27 = 1 << 27;
            uint256 internal constant _ROLE_28 = 1 << 28;
            uint256 internal constant _ROLE_29 = 1 << 29;
            uint256 internal constant _ROLE_30 = 1 << 30;
            uint256 internal constant _ROLE_31 = 1 << 31;
            uint256 internal constant _ROLE_32 = 1 << 32;
            uint256 internal constant _ROLE_33 = 1 << 33;
            uint256 internal constant _ROLE_34 = 1 << 34;
            uint256 internal constant _ROLE_35 = 1 << 35;
            uint256 internal constant _ROLE_36 = 1 << 36;
            uint256 internal constant _ROLE_37 = 1 << 37;
            uint256 internal constant _ROLE_38 = 1 << 38;
            uint256 internal constant _ROLE_39 = 1 << 39;
            uint256 internal constant _ROLE_40 = 1 << 40;
            uint256 internal constant _ROLE_41 = 1 << 41;
            uint256 internal constant _ROLE_42 = 1 << 42;
            uint256 internal constant _ROLE_43 = 1 << 43;
            uint256 internal constant _ROLE_44 = 1 << 44;
            uint256 internal constant _ROLE_45 = 1 << 45;
            uint256 internal constant _ROLE_46 = 1 << 46;
            uint256 internal constant _ROLE_47 = 1 << 47;
            uint256 internal constant _ROLE_48 = 1 << 48;
            uint256 internal constant _ROLE_49 = 1 << 49;
            uint256 internal constant _ROLE_50 = 1 << 50;
            uint256 internal constant _ROLE_51 = 1 << 51;
            uint256 internal constant _ROLE_52 = 1 << 52;
            uint256 internal constant _ROLE_53 = 1 << 53;
            uint256 internal constant _ROLE_54 = 1 << 54;
            uint256 internal constant _ROLE_55 = 1 << 55;
            uint256 internal constant _ROLE_56 = 1 << 56;
            uint256 internal constant _ROLE_57 = 1 << 57;
            uint256 internal constant _ROLE_58 = 1 << 58;
            uint256 internal constant _ROLE_59 = 1 << 59;
            uint256 internal constant _ROLE_60 = 1 << 60;
            uint256 internal constant _ROLE_61 = 1 << 61;
            uint256 internal constant _ROLE_62 = 1 << 62;
            uint256 internal constant _ROLE_63 = 1 << 63;
            uint256 internal constant _ROLE_64 = 1 << 64;
            uint256 internal constant _ROLE_65 = 1 << 65;
            uint256 internal constant _ROLE_66 = 1 << 66;
            uint256 internal constant _ROLE_67 = 1 << 67;
            uint256 internal constant _ROLE_68 = 1 << 68;
            uint256 internal constant _ROLE_69 = 1 << 69;
            uint256 internal constant _ROLE_70 = 1 << 70;
            uint256 internal constant _ROLE_71 = 1 << 71;
            uint256 internal constant _ROLE_72 = 1 << 72;
            uint256 internal constant _ROLE_73 = 1 << 73;
            uint256 internal constant _ROLE_74 = 1 << 74;
            uint256 internal constant _ROLE_75 = 1 << 75;
            uint256 internal constant _ROLE_76 = 1 << 76;
            uint256 internal constant _ROLE_77 = 1 << 77;
            uint256 internal constant _ROLE_78 = 1 << 78;
            uint256 internal constant _ROLE_79 = 1 << 79;
            uint256 internal constant _ROLE_80 = 1 << 80;
            uint256 internal constant _ROLE_81 = 1 << 81;
            uint256 internal constant _ROLE_82 = 1 << 82;
            uint256 internal constant _ROLE_83 = 1 << 83;
            uint256 internal constant _ROLE_84 = 1 << 84;
            uint256 internal constant _ROLE_85 = 1 << 85;
            uint256 internal constant _ROLE_86 = 1 << 86;
            uint256 internal constant _ROLE_87 = 1 << 87;
            uint256 internal constant _ROLE_88 = 1 << 88;
            uint256 internal constant _ROLE_89 = 1 << 89;
            uint256 internal constant _ROLE_90 = 1 << 90;
            uint256 internal constant _ROLE_91 = 1 << 91;
            uint256 internal constant _ROLE_92 = 1 << 92;
            uint256 internal constant _ROLE_93 = 1 << 93;
            uint256 internal constant _ROLE_94 = 1 << 94;
            uint256 internal constant _ROLE_95 = 1 << 95;
            uint256 internal constant _ROLE_96 = 1 << 96;
            uint256 internal constant _ROLE_97 = 1 << 97;
            uint256 internal constant _ROLE_98 = 1 << 98;
            uint256 internal constant _ROLE_99 = 1 << 99;
            uint256 internal constant _ROLE_100 = 1 << 100;
            uint256 internal constant _ROLE_101 = 1 << 101;
            uint256 internal constant _ROLE_102 = 1 << 102;
            uint256 internal constant _ROLE_103 = 1 << 103;
            uint256 internal constant _ROLE_104 = 1 << 104;
            uint256 internal constant _ROLE_105 = 1 << 105;
            uint256 internal constant _ROLE_106 = 1 << 106;
            uint256 internal constant _ROLE_107 = 1 << 107;
            uint256 internal constant _ROLE_108 = 1 << 108;
            uint256 internal constant _ROLE_109 = 1 << 109;
            uint256 internal constant _ROLE_110 = 1 << 110;
            uint256 internal constant _ROLE_111 = 1 << 111;
            uint256 internal constant _ROLE_112 = 1 << 112;
            uint256 internal constant _ROLE_113 = 1 << 113;
            uint256 internal constant _ROLE_114 = 1 << 114;
            uint256 internal constant _ROLE_115 = 1 << 115;
            uint256 internal constant _ROLE_116 = 1 << 116;
            uint256 internal constant _ROLE_117 = 1 << 117;
            uint256 internal constant _ROLE_118 = 1 << 118;
            uint256 internal constant _ROLE_119 = 1 << 119;
            uint256 internal constant _ROLE_120 = 1 << 120;
            uint256 internal constant _ROLE_121 = 1 << 121;
            uint256 internal constant _ROLE_122 = 1 << 122;
            uint256 internal constant _ROLE_123 = 1 << 123;
            uint256 internal constant _ROLE_124 = 1 << 124;
            uint256 internal constant _ROLE_125 = 1 << 125;
            uint256 internal constant _ROLE_126 = 1 << 126;
            uint256 internal constant _ROLE_127 = 1 << 127;
            uint256 internal constant _ROLE_128 = 1 << 128;
            uint256 internal constant _ROLE_129 = 1 << 129;
            uint256 internal constant _ROLE_130 = 1 << 130;
            uint256 internal constant _ROLE_131 = 1 << 131;
            uint256 internal constant _ROLE_132 = 1 << 132;
            uint256 internal constant _ROLE_133 = 1 << 133;
            uint256 internal constant _ROLE_134 = 1 << 134;
            uint256 internal constant _ROLE_135 = 1 << 135;
            uint256 internal constant _ROLE_136 = 1 << 136;
            uint256 internal constant _ROLE_137 = 1 << 137;
            uint256 internal constant _ROLE_138 = 1 << 138;
            uint256 internal constant _ROLE_139 = 1 << 139;
            uint256 internal constant _ROLE_140 = 1 << 140;
            uint256 internal constant _ROLE_141 = 1 << 141;
            uint256 internal constant _ROLE_142 = 1 << 142;
            uint256 internal constant _ROLE_143 = 1 << 143;
            uint256 internal constant _ROLE_144 = 1 << 144;
            uint256 internal constant _ROLE_145 = 1 << 145;
            uint256 internal constant _ROLE_146 = 1 << 146;
            uint256 internal constant _ROLE_147 = 1 << 147;
            uint256 internal constant _ROLE_148 = 1 << 148;
            uint256 internal constant _ROLE_149 = 1 << 149;
            uint256 internal constant _ROLE_150 = 1 << 150;
            uint256 internal constant _ROLE_151 = 1 << 151;
            uint256 internal constant _ROLE_152 = 1 << 152;
            uint256 internal constant _ROLE_153 = 1 << 153;
            uint256 internal constant _ROLE_154 = 1 << 154;
            uint256 internal constant _ROLE_155 = 1 << 155;
            uint256 internal constant _ROLE_156 = 1 << 156;
            uint256 internal constant _ROLE_157 = 1 << 157;
            uint256 internal constant _ROLE_158 = 1 << 158;
            uint256 internal constant _ROLE_159 = 1 << 159;
            uint256 internal constant _ROLE_160 = 1 << 160;
            uint256 internal constant _ROLE_161 = 1 << 161;
            uint256 internal constant _ROLE_162 = 1 << 162;
            uint256 internal constant _ROLE_163 = 1 << 163;
            uint256 internal constant _ROLE_164 = 1 << 164;
            uint256 internal constant _ROLE_165 = 1 << 165;
            uint256 internal constant _ROLE_166 = 1 << 166;
            uint256 internal constant _ROLE_167 = 1 << 167;
            uint256 internal constant _ROLE_168 = 1 << 168;
            uint256 internal constant _ROLE_169 = 1 << 169;
            uint256 internal constant _ROLE_170 = 1 << 170;
            uint256 internal constant _ROLE_171 = 1 << 171;
            uint256 internal constant _ROLE_172 = 1 << 172;
            uint256 internal constant _ROLE_173 = 1 << 173;
            uint256 internal constant _ROLE_174 = 1 << 174;
            uint256 internal constant _ROLE_175 = 1 << 175;
            uint256 internal constant _ROLE_176 = 1 << 176;
            uint256 internal constant _ROLE_177 = 1 << 177;
            uint256 internal constant _ROLE_178 = 1 << 178;
            uint256 internal constant _ROLE_179 = 1 << 179;
            uint256 internal constant _ROLE_180 = 1 << 180;
            uint256 internal constant _ROLE_181 = 1 << 181;
            uint256 internal constant _ROLE_182 = 1 << 182;
            uint256 internal constant _ROLE_183 = 1 << 183;
            uint256 internal constant _ROLE_184 = 1 << 184;
            uint256 internal constant _ROLE_185 = 1 << 185;
            uint256 internal constant _ROLE_186 = 1 << 186;
            uint256 internal constant _ROLE_187 = 1 << 187;
            uint256 internal constant _ROLE_188 = 1 << 188;
            uint256 internal constant _ROLE_189 = 1 << 189;
            uint256 internal constant _ROLE_190 = 1 << 190;
            uint256 internal constant _ROLE_191 = 1 << 191;
            uint256 internal constant _ROLE_192 = 1 << 192;
            uint256 internal constant _ROLE_193 = 1 << 193;
            uint256 internal constant _ROLE_194 = 1 << 194;
            uint256 internal constant _ROLE_195 = 1 << 195;
            uint256 internal constant _ROLE_196 = 1 << 196;
            uint256 internal constant _ROLE_197 = 1 << 197;
            uint256 internal constant _ROLE_198 = 1 << 198;
            uint256 internal constant _ROLE_199 = 1 << 199;
            uint256 internal constant _ROLE_200 = 1 << 200;
            uint256 internal constant _ROLE_201 = 1 << 201;
            uint256 internal constant _ROLE_202 = 1 << 202;
            uint256 internal constant _ROLE_203 = 1 << 203;
            uint256 internal constant _ROLE_204 = 1 << 204;
            uint256 internal constant _ROLE_205 = 1 << 205;
            uint256 internal constant _ROLE_206 = 1 << 206;
            uint256 internal constant _ROLE_207 = 1 << 207;
            uint256 internal constant _ROLE_208 = 1 << 208;
            uint256 internal constant _ROLE_209 = 1 << 209;
            uint256 internal constant _ROLE_210 = 1 << 210;
            uint256 internal constant _ROLE_211 = 1 << 211;
            uint256 internal constant _ROLE_212 = 1 << 212;
            uint256 internal constant _ROLE_213 = 1 << 213;
            uint256 internal constant _ROLE_214 = 1 << 214;
            uint256 internal constant _ROLE_215 = 1 << 215;
            uint256 internal constant _ROLE_216 = 1 << 216;
            uint256 internal constant _ROLE_217 = 1 << 217;
            uint256 internal constant _ROLE_218 = 1 << 218;
            uint256 internal constant _ROLE_219 = 1 << 219;
            uint256 internal constant _ROLE_220 = 1 << 220;
            uint256 internal constant _ROLE_221 = 1 << 221;
            uint256 internal constant _ROLE_222 = 1 << 222;
            uint256 internal constant _ROLE_223 = 1 << 223;
            uint256 internal constant _ROLE_224 = 1 << 224;
            uint256 internal constant _ROLE_225 = 1 << 225;
            uint256 internal constant _ROLE_226 = 1 << 226;
            uint256 internal constant _ROLE_227 = 1 << 227;
            uint256 internal constant _ROLE_228 = 1 << 228;
            uint256 internal constant _ROLE_229 = 1 << 229;
            uint256 internal constant _ROLE_230 = 1 << 230;
            uint256 internal constant _ROLE_231 = 1 << 231;
            uint256 internal constant _ROLE_232 = 1 << 232;
            uint256 internal constant _ROLE_233 = 1 << 233;
            uint256 internal constant _ROLE_234 = 1 << 234;
            uint256 internal constant _ROLE_235 = 1 << 235;
            uint256 internal constant _ROLE_236 = 1 << 236;
            uint256 internal constant _ROLE_237 = 1 << 237;
            uint256 internal constant _ROLE_238 = 1 << 238;
            uint256 internal constant _ROLE_239 = 1 << 239;
            uint256 internal constant _ROLE_240 = 1 << 240;
            uint256 internal constant _ROLE_241 = 1 << 241;
            uint256 internal constant _ROLE_242 = 1 << 242;
            uint256 internal constant _ROLE_243 = 1 << 243;
            uint256 internal constant _ROLE_244 = 1 << 244;
            uint256 internal constant _ROLE_245 = 1 << 245;
            uint256 internal constant _ROLE_246 = 1 << 246;
            uint256 internal constant _ROLE_247 = 1 << 247;
            uint256 internal constant _ROLE_248 = 1 << 248;
            uint256 internal constant _ROLE_249 = 1 << 249;
            uint256 internal constant _ROLE_250 = 1 << 250;
            uint256 internal constant _ROLE_251 = 1 << 251;
            uint256 internal constant _ROLE_252 = 1 << 252;
            uint256 internal constant _ROLE_253 = 1 << 253;
            uint256 internal constant _ROLE_254 = 1 << 254;
            uint256 internal constant _ROLE_255 = 1 << 255;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeApproveCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeApproveCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeApproveERC721 hook that is called by a core token before approving a token.
             *
             *  @param _from The address that is approving tokens.
             *  @param _to The address that is being approved.
             *  @param _tokenId The token ID being approved.
             *  @param _approve The approval status to set.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeApproveERC721(address _from, address _to, uint256 _tokenId, bool _approve)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeApproveCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeApproveForAllCallback {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeApproveForAllCallbackNotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeApproveForAll hook that is called by a core token before approving an operator to transfer all tokens.
             *
             *  @param _from The address that is approving tokens.
             *  @param _to The address that is being approved.
             *  @param _approved Whether to grant or revoke approval.
             */
            function beforeApproveForAll(address _from, address _to, bool _approved)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeApproveForAllCallbackNotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeBurnCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeBurnCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeBurnERC721 hook that is called by a core token before burning a token.
             *
             *  @param _tokenId The token ID being burned.
             *  @param _data The encoded arguments for the beforeBurn hook.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeBurnERC721(uint256 _tokenId, bytes memory _data)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert BeforeBurnCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeMintCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeMintCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _startTokenId The token ID being minted.
             *  @param _amount The amount of tokens to mint.
             *  @param _data Optional extra data passed to the hook.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeMintERC721(address _to, uint256 _startTokenId, uint256 _amount, bytes memory _data)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert BeforeMintCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeMintWithSignatureCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeMintWithSignatureCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintWithSignatureERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _startTokenId The token ID being minted.
             *  @param _amount The amount of tokens to mint.
             *  @param _data Optional extra data passed to the hook.
             *  @param _signer The address that signed the minting request.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeMintWithSignatureERC721(
                address _to,
                uint256 _startTokenId,
                uint256 _amount,
                bytes memory _data,
                address _signer
            ) external payable virtual returns (bytes memory result) {
                revert BeforeMintWithSignatureCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeTransferCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeTransferCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeTransferERC721 hook that is called by a core token before transferring a token.
             *
             *  @param _from The address that is transferring tokens.
             *  @param _to The address that is receiving tokens.
             *  @param _tokenId The token ID being transferred.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeTransferERC721(address _from, address _to, uint256 _tokenId)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeTransferCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract OnTokenURICallback {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error OnTokenURICallbackNotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Returns the URI to fetch token metadata from.
             *  @dev Meant to be called by the core token contract.
             *  @param _tokenId The token ID of the NFT.
             *  @return metadata The URI to fetch token metadata from.
             */
            function onTokenURI(uint256 _tokenId) external view virtual returns (string memory metadata) {
                revert OnTokenURICallbackNotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract UpdateMetadataCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error UpdateMetadataCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _amount The amount of tokens to mint.
             *  @param _baseURI The URI to fetch token metadata from.
             *  @return result Abi encoded bytes result of the hook.
             */
            function updateMetadataERC721(address _to, uint256 _startTokenId, uint256 _amount, string calldata _baseURI)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert UpdateMetadataCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {
            ERC721AQueryableUpgradeable,
            ERC721AUpgradeable,
            IERC721AUpgradeable
        } from "@erc721a-upgradeable/extensions/ERC721AQueryableUpgradeable.sol";
        import {ECDSA} from "@solady/utils/ECDSA.sol";
        import {EIP712} from "@solady/utils/EIP712.sol";
        import {Multicallable} from "@solady/utils/Multicallable.sol";
        import {Core} from "../../Core.sol";
        import {BeforeApproveCallbackERC721} from "../../callback/BeforeApproveCallbackERC721.sol";
        import {BeforeApproveForAllCallback} from "../../callback/BeforeApproveForAllCallback.sol";
        import {BeforeBurnCallbackERC721} from "../../callback/BeforeBurnCallbackERC721.sol";
        import {BeforeMintCallbackERC721} from "../../callback/BeforeMintCallbackERC721.sol";
        import {BeforeMintWithSignatureCallbackERC721} from "../../callback/BeforeMintWithSignatureCallbackERC721.sol";
        import {BeforeTransferCallbackERC721} from "../../callback/BeforeTransferCallbackERC721.sol";
        import {UpdateMetadataCallbackERC721} from "../../callback/UpdateMetadataCallbackERC721.sol";
        import {OnTokenURICallback} from "../../callback/OnTokenURICallback.sol";
        contract ERC721Base is ERC721AQueryableUpgradeable, Core, Multicallable, EIP712 {
            using ECDSA for bytes32;
            /*//////////////////////////////////////////////////////////////
                                        CONSTANTS
            //////////////////////////////////////////////////////////////*/
            bytes32 private constant TYPEHASH_SIGNATURE_MINT_ERC721 =
                keccak256("MintRequestERC721(address to,uint256 amount,string baseURI,bytes data)");
            /*//////////////////////////////////////////////////////////////
                                        STORAGE
            //////////////////////////////////////////////////////////////*/
            /// @notice The contract metadata URI of the contract.
            string private contractURI_;
            /*//////////////////////////////////////////////////////////////
                                       EVENTS
            //////////////////////////////////////////////////////////////*/
            /// @notice Emitted when the contract URI is updated.
            event ContractURIUpdated();
            /*//////////////////////////////////////////////////////////////
                                    CONSTRUCTOR
            //////////////////////////////////////////////////////////////*/
            function _initialize(
                string memory _name,
                string memory _symbol,
                string memory _contractURI,
                address _owner,
                address[] memory _modules,
                bytes[] memory _moduleInstallData
            ) internal initializerERC721A {
                // Set contract metadata
                __ERC721A_init(_name, _symbol);
                _setupContractURI(_contractURI);
                _initializeOwner(_owner);
                // Install and initialize modules
                require(_modules.length == _moduleInstallData.length);
                for (uint256 i = 0; i < _modules.length; i++) {
                    _installModule(_modules[i], _moduleInstallData[i]);
                }
            }
            /*//////////////////////////////////////////////////////////////
                                      VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Returns the contract URI of the contract.
             *  @return uri The contract URI of the contract.
             */
            function contractURI() external view returns (string memory) {
                return contractURI_;
            }
            /// @notice Returns the starting token ID for sequential mints.
            function startTokenId() external view returns (uint256) {
                return _startTokenId();
            }
            /// @notice Returns the total number of tokens minted ever.
            function totalMinted() external view returns (uint256) {
                return _totalMinted();
            }
            /**
             *  @notice Returns the token metadata of an NFT.
             *  @dev Always returns metadata queried from the metadata source.
             *  @param id The token ID of the NFT.
             *  @return metadata The URI to fetch metadata from.
             */
            function tokenURI(uint256 id)
                public
                view
                override(ERC721AUpgradeable, IERC721AUpgradeable)
                returns (string memory)
            {
                return _getTokenURI(id);
            }
            /**
             *  @notice Returns whether the contract implements an interface with the given interface ID.
             *  @param interfaceId The interface ID of the interface to check for
             */
            function supportsInterface(bytes4 interfaceId)
                public
                view
                override(ERC721AUpgradeable, IERC721AUpgradeable, Core)
                returns (bool)
            {
                return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
                    || interfaceId == 0x80ac58cd // ERC165 Interface ID for ERC721
                    || interfaceId == 0x5b5e139f // ERC165 Interface ID for ERC721Metadata
                    || interfaceId == 0xe8a3d485 // ERC-7572
                    || interfaceId == 0x7f5828d0 // ERC-173
                    || super.supportsInterface(interfaceId); // right-most Core
            }
            function getSupportedCallbackFunctions()
                public
                pure
                override
                returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
            {
                supportedCallbackFunctions = new SupportedCallbackFunction[](8);
                supportedCallbackFunctions[0] = SupportedCallbackFunction({
                    selector: BeforeMintCallbackERC721.beforeMintERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
                supportedCallbackFunctions[1] = SupportedCallbackFunction({
                    selector: BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
                supportedCallbackFunctions[2] = SupportedCallbackFunction({
                    selector: BeforeTransferCallbackERC721.beforeTransferERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[3] = SupportedCallbackFunction({
                    selector: BeforeBurnCallbackERC721.beforeBurnERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[4] = SupportedCallbackFunction({
                    selector: BeforeApproveCallbackERC721.beforeApproveERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[5] = SupportedCallbackFunction({
                    selector: BeforeApproveForAllCallback.beforeApproveForAll.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[6] =
                    SupportedCallbackFunction({selector: OnTokenURICallback.onTokenURI.selector, mode: CallbackMode.REQUIRED});
                supportedCallbackFunctions[7] = SupportedCallbackFunction({
                    selector: UpdateMetadataCallbackERC721.updateMetadataERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
            }
            /*//////////////////////////////////////////////////////////////
                                EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Sets the contract URI of the contract.
             *  @dev Only callable by contract admin.
             *  @param uri The contract URI to set.
             */
            function setContractURI(string memory uri) external onlyOwner {
                _setupContractURI(uri);
            }
            /**
             *  @notice Mints a token. Calls the beforeMint hook.
             *  @dev Reverts if beforeMint hook is absent or unsuccessful.
             *  @param to The address to mint the token to.
             *  @param amount The amount of tokens to mint.
             *  @param data ABI encoded data to pass to the beforeMint hook.
             */
            function mint(address to, uint256 amount, string calldata baseURI, bytes calldata data) external payable {
                uint256 tokenId = _nextTokenId();
                if (bytes(baseURI).length > 0) {
                    _updateMetadata(to, tokenId, amount, baseURI);
                }
                _beforeMint(to, tokenId, amount, data);
                _safeMint(to, amount, "");
            }
            /**
             *  @notice Mints a token with a signature. Calls the beforeMint hook.
             *  @dev Reverts if beforeMint hook is absent or unsuccessful.
             *  @param to The address to mint the token to.
             *  @param amount The amount of tokens to mint.
             *  @param data ABI encoded data to pass to the beforeMint hook.
             *  @param signature The signature produced from signing the minting request.
             */
            function mintWithSignature(
                address to,
                uint256 amount,
                string calldata baseURI,
                bytes calldata data,
                bytes memory signature
            ) external payable {
                address signer = _hashTypedData(
                    keccak256(
                        abi.encode(TYPEHASH_SIGNATURE_MINT_ERC721, to, amount, keccak256(bytes(baseURI)), keccak256(data))
                    )
                ).recover(signature);
                uint256 tokenId = _nextTokenId();
                if (bytes(baseURI).length > 0) {
                    _updateMetadata(to, tokenId, amount, baseURI);
                }
                _beforeMintWithSignature(to, tokenId, amount, data, signer);
                _safeMint(to, amount, "");
            }
            /**
             *  @notice Burns an NFT.
             *  @dev Calls the beforeBurn hook. Skips calling the hook if it doesn't exist.
             *  @param tokenId The token ID of the NFT to burn.
             *  @param data ABI encoded data to pass to the beforeBurn hook.
             */
            function burn(uint256 tokenId, bytes calldata data) external payable {
                _beforeBurn(tokenId, data);
                _burn(tokenId, true);
            }
            /**
             *  @notice Transfers ownership of an NFT from one address to another.
             *  @dev Overriden to call the beforeTransfer hook. Skips calling the hook if it doesn't exist.
             *  @param from The address to transfer from
             *  @param to The address to transfer to
             *  @param id The token ID of the NFT
             */
            function transferFrom(address from, address to, uint256 id)
                public
                payable
                override(ERC721AUpgradeable, IERC721AUpgradeable)
            {
                _beforeTransfer(from, to, id);
                super.transferFrom(from, to, id);
            }
            /**
             *  @notice Approves an address to transfer a specific NFT. Reverts if caller is not owner or approved operator.
             *  @dev Overriden to call the beforeApprove hook. Skips calling the hook if it doesn't exist.
             *  @param spender The address to approve
             *  @param id The token ID of the NFT
             */
            function approve(address spender, uint256 id) public payable override(ERC721AUpgradeable, IERC721AUpgradeable) {
                _beforeApprove(msg.sender, spender, id, true);
                super.approve(spender, id);
            }
            /**
             *  @notice Approves or revokes approval from an operator to transfer or issue approval for all of the caller's NFTs.
             *  @param operator The address to approve or revoke approval from
             *  @param approved Whether the operator is approved
             */
            function setApprovalForAll(address operator, bool approved)
                public
                override(ERC721AUpgradeable, IERC721AUpgradeable)
            {
                _beforeApproveForAll(msg.sender, operator, approved);
                super.setApprovalForAll(operator, approved);
            }
            /*//////////////////////////////////////////////////////////////
                                    INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Sets contract URI
            function _setupContractURI(string memory _contractURI) internal {
                contractURI_ = _contractURI;
                emit ContractURIUpdated();
            }
            /*//////////////////////////////////////////////////////////////
                                CALLBACK INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Calls the beforeMint hook.
            function _beforeMint(address to, uint256 startTokenId, uint256 amount, bytes calldata data) internal virtual {
                _executeCallbackFunction(
                    BeforeMintCallbackERC721.beforeMintERC721.selector,
                    abi.encodeCall(BeforeMintCallbackERC721.beforeMintERC721, (to, startTokenId, amount, data))
                );
            }
            /// @dev Calls the beforeMint hook.
            function _beforeMintWithSignature(
                address to,
                uint256 startTokenId,
                uint256 amount,
                bytes calldata data,
                address signer
            ) internal virtual {
                _executeCallbackFunction(
                    BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721.selector,
                    abi.encodeCall(
                        BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721,
                        (to, startTokenId, amount, data, signer)
                    )
                );
            }
            /// @dev Calls the beforeTransfer hook, if installed.
            function _beforeTransfer(address from, address to, uint256 tokenId) internal virtual {
                _executeCallbackFunction(
                    BeforeTransferCallbackERC721.beforeTransferERC721.selector,
                    abi.encodeCall(BeforeTransferCallbackERC721.beforeTransferERC721, (from, to, tokenId))
                );
            }
            /// @dev Calls the beforeBurn hook, if installed.
            function _beforeBurn(uint256 tokenId, bytes calldata data) internal virtual {
                _executeCallbackFunction(
                    BeforeBurnCallbackERC721.beforeBurnERC721.selector,
                    abi.encodeCall(BeforeBurnCallbackERC721.beforeBurnERC721, (tokenId, data))
                );
            }
            /// @dev Calls the beforeApprove hook, if installed.
            function _beforeApprove(address from, address to, uint256 tokenId, bool approved) internal virtual {
                _executeCallbackFunction(
                    BeforeApproveCallbackERC721.beforeApproveERC721.selector,
                    abi.encodeCall(BeforeApproveCallbackERC721.beforeApproveERC721, (from, to, tokenId, approved))
                );
            }
            /// @dev Calls the beforeApprove hook, if installed.
            function _beforeApproveForAll(address from, address to, bool approved) internal virtual {
                _executeCallbackFunction(
                    BeforeApproveForAllCallback.beforeApproveForAll.selector,
                    abi.encodeCall(BeforeApproveForAllCallback.beforeApproveForAll, (from, to, approved))
                );
            }
            /// @dev Calls the updateMetadata hook, if installed.
            function _updateMetadata(address to, uint256 startTokenId, uint256 amount, string calldata baseURI)
                internal
                virtual
            {
                _executeCallbackFunction(
                    UpdateMetadataCallbackERC721.updateMetadataERC721.selector,
                    abi.encodeCall(UpdateMetadataCallbackERC721.updateMetadataERC721, (to, startTokenId, amount, baseURI))
                );
            }
            /// @dev Fetches token URI from the token metadata hook.
            function _getTokenURI(uint256 tokenId) internal view virtual returns (string memory uri) {
                (, bytes memory returndata) = _executeCallbackFunctionView(
                    OnTokenURICallback.onTokenURI.selector, abi.encodeCall(OnTokenURICallback.onTokenURI, (tokenId))
                );
                uri = abi.decode(returndata, (string));
            }
            /// @dev Returns the domain name and version for EIP712.
            function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
                name = "ERC721Core";
                version = "1";
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {ERC721Base} from "./ERC721Base.sol";
        import {Initializable} from "@solady/utils/Initializable.sol";
        contract ERC721CoreInitializable is ERC721Base, Initializable {
            constructor() {
                _disableInitializers();
            }
            function initialize(
                string memory _name,
                string memory _symbol,
                string memory _contractURI,
                address _owner,
                address[] memory _modules,
                bytes[] memory _moduleInstallData
            ) external payable initializer {
                _initialize(_name, _symbol, _contractURI, _owner, _modules, _moduleInstallData);
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {IERC165} from "./IERC165.sol";
        import {IModuleConfig} from "./IModuleConfig.sol";
        interface ICore is IModuleConfig, IERC165 {
            /*//////////////////////////////////////////////////////////////
                                    STRUCTS & ENUMS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Whether execution reverts when the callback function is not implemented by any installed Module.
             *  @param OPTIONAL Execution does not revert when the callback function is not implemented.
             *  @param REQUIRED Execution reverts when the callback function is not implemented.
             */
            enum CallbackMode {
                OPTIONAL,
                REQUIRED
            }
            /**
             *  @dev Struct representing a callback function called on an Module during some fixed function's execution.
             *  @param selector The 4-byte function selector of the callback function.
             *  @param mode Whether execution reverts when the callback function is not implemented by any installed Module.
             */
            struct SupportedCallbackFunction {
                bytes4 selector;
                CallbackMode mode;
            }
            /**
             *  @dev Struct representing an installed Module.
             *  @param implementation The address of the Module contract.
             *  @param config The Module Config of the Module contract.
             */
            struct InstalledModule {
                address implementation;
                ModuleConfig config;
            }
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Returns all callback function calls made to Modules at some point during a fixed function's execution.
            function getSupportedCallbackFunctions() external pure returns (SupportedCallbackFunction[] memory);
            /// @dev Returns all installed modules and their respective module configs.
            function getInstalledModules() external view returns (InstalledModule[] memory);
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Installs an Module in the Core.
             *
             *  @param moduleContract The address of the Module contract to be installed.
             *  @param data The data to be passed to the Module's onInstall callback function.
             *
             *  MUST implement authorization control.
             *  MUST call `onInstall` callback function if Module Config has registerd for installation callbacks.
             *  MUST revert if Core does not implement the interface required by the Module, specified in the Module Config.
             *  MUST revert if any callback or fallback function in the Module's ModuleConfig is already registered in the Core with another Module.
             *
             *  MAY interpret the provided address as the implementation address of the Module contract to install as a proxy.
             */
            function installModule(address moduleContract, bytes calldata data) external payable;
            /**
             *  @dev Uninstalls an Module from the Core.
             *
             *  @param moduleContract The address of the Module contract to be uninstalled.
             *  @param data The data to be passed to the Module's onUninstall callback function.
             *
             *  MUST implement authorization control.
             *  MUST call `onUninstall` callback function if Module Config has registerd for installation callbacks.
             *
             *  MAY interpret the provided address as the implementation address of the Module contract which is installed as a proxy.
             */
            function uninstallModule(address moduleContract, bytes calldata data) external payable;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IERC165 {
            /// @notice Query if a contract implements an interface
            /// @param interfaceID The interface identifier, as specified in ERC-165
            /// @dev Interface identification is specified in ERC-165. This function
            ///  uses less than 30,000 gas.
            /// @return `true` if the contract implements `interfaceID` and
            ///  `interfaceID` is not 0xffffffff, `false` otherwise
            function supportsInterface(bytes4 interfaceID) external view returns (bool);
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IInstallationCallback {
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Called by a Core into an Module during the installation of the Module.
             *
             *  @param data The data passed to the Core's installModule function.
             */
            function onInstall(bytes calldata data) external;
            /**
             *  @dev Called by a Core into an Module during the uninstallation of the Module.
             *
             *  @param data The data passed to the Core's uninstallModule function.
             */
            function onUninstall(bytes calldata data) external;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {IModuleConfig} from "./IModuleConfig.sol";
        interface IModule is IModuleConfig {
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Returns the ModuleConfig of the Module contract.
             */
            function getModuleConfig() external pure returns (ModuleConfig memory);
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IModuleConfig {
            /*//////////////////////////////////////////////////////////////
                                    STRUCTS & ENUMS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Struct for a callback function. Called by a Core into an Module during the execution of some fixed function.
             *
             *  @param selector The 4-byte selector of the function.
             *  @param callType The type of call to be made to the function.
             */
            struct CallbackFunction {
                bytes4 selector;
            }
            /**
             *  @dev Struct for a fallback function. Called by a Core into an Module via the Core's fallback.
             *
             *  @param selector The 4-byte selector of the function.
             *  @param callType The type of call to be made to the function.
             *  @param permissionBits Core’s fallback function MUST check that msg.sender has these permissions before
             *                        performing a call on the Module. (OPTIONAL field)
             */
            struct FallbackFunction {
                bytes4 selector;
                uint256 permissionBits;
            }
            /**
             *  @dev Struct containing all information that a Core uses to check whether an Module is compatible for installation.
             *
             *  @param registerInstallationCallback Whether the Module expects onInstall and onUninstall callback function calls at
             *                                      installation and uninstallation time, respectively
             *  @param requiredInterfaces The ERC-165 interface that a Core MUST support to be compatible for installation. OPTIONAL -- can be bytes4(0)
             *                             if there is no required interface id.
             *  @param supportedInterfaces The ERC-165 interfaces that a Core supports upon installing the Module.
             *  @param callbackFunctions List of callback functions that the Core MUST call at some point in the execution of its fixed functions.
             *  @param fallbackFunctions List of functions that the Core MUST call via its fallback function with the Module as the call destination.
             */
            struct ModuleConfig {
                bool registerInstallationCallback;
                bytes4[] requiredInterfaces;
                bytes4[] supportedInterfaces;
                CallbackFunction[] callbackFunctions;
                FallbackFunction[] fallbackFunctions;
            }
        }
        

        File 3 of 3: ERC721CoreInitializable
        // 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
        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
        // 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
        // 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
        pragma solidity ^0.8.4;
        /// @notice Simple single owner authorization mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
        ///
        /// @dev Note:
        /// This implementation does NOT auto-initialize the owner to `msg.sender`.
        /// You MUST call the `_initializeOwner` in the constructor / initializer.
        ///
        /// While the ownable portion follows
        /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
        /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
        abstract contract Ownable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The caller is not authorized to call the function.
            error Unauthorized();
            /// @dev The `newOwner` cannot be the zero address.
            error NewOwnerIsZeroAddress();
            /// @dev The `pendingOwner` does not have a valid handover request.
            error NoHandoverRequest();
            /// @dev Cannot double-initialize.
            error AlreadyInitialized();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
            /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
            /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
            /// despite it not being as lightweight as a single argument event.
            event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
            /// @dev An ownership handover to `pendingOwner` has been requested.
            event OwnershipHandoverRequested(address indexed pendingOwner);
            /// @dev The ownership handover to `pendingOwner` has been canceled.
            event OwnershipHandoverCanceled(address indexed pendingOwner);
            /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
            uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
                0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
            /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
            uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
                0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
            /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
            uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
                0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The owner slot is given by:
            /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
            /// It is intentionally chosen to be a high value
            /// to avoid collision with lower slots.
            /// The choice of manual storage layout is to enable compatibility
            /// with both regular and upgradeable contracts.
            bytes32 internal constant _OWNER_SLOT =
                0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
            /// The ownership handover slot of `newOwner` is given by:
            /// ```
            ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
            ///     let handoverSlot := keccak256(0x00, 0x20)
            /// ```
            /// It stores the expiry timestamp of the two-step ownership handover.
            uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     INTERNAL FUNCTIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
            function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
            /// @dev Initializes the owner directly without authorization guard.
            /// This function must be called upon initialization,
            /// regardless of whether the contract is upgradeable or not.
            /// This is to enable generalization to both regular and upgradeable contracts,
            /// and to save gas in case the initial owner is not the caller.
            /// For performance reasons, this function will not check if there
            /// is an existing owner.
            function _initializeOwner(address newOwner) internal virtual {
                if (_guardInitializeOwner()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        if sload(ownerSlot) {
                            mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                            revert(0x1c, 0x04)
                        }
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Store the new value.
                        sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                    }
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Store the new value.
                        sstore(_OWNER_SLOT, newOwner)
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                    }
                }
            }
            /// @dev Sets the owner directly without authorization guard.
            function _setOwner(address newOwner) internal virtual {
                if (_guardInitializeOwner()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                        // Store the new value.
                        sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                    }
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ownerSlot := _OWNER_SLOT
                        // Clean the upper 96 bits.
                        newOwner := shr(96, shl(96, newOwner))
                        // Emit the {OwnershipTransferred} event.
                        log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                        // Store the new value.
                        sstore(ownerSlot, newOwner)
                    }
                }
            }
            /// @dev Throws if the sender is not the owner.
            function _checkOwner() internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // If the caller is not the stored owner, revert.
                    if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Returns how long a two-step ownership handover is valid for in seconds.
            /// Override to return a different value if needed.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
                return 48 * 3600;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  PUBLIC UPDATE FUNCTIONS                   */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Allows the owner to transfer the ownership to `newOwner`.
            function transferOwnership(address newOwner) public payable virtual onlyOwner {
                /// @solidity memory-safe-assembly
                assembly {
                    if iszero(shl(96, newOwner)) {
                        mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                        revert(0x1c, 0x04)
                    }
                }
                _setOwner(newOwner);
            }
            /// @dev Allows the owner to renounce their ownership.
            function renounceOwnership() public payable virtual onlyOwner {
                _setOwner(address(0));
            }
            /// @dev Request a two-step ownership handover to the caller.
            /// The request will automatically expire in 48 hours (172800 seconds) by default.
            function requestOwnershipHandover() public payable virtual {
                unchecked {
                    uint256 expires = block.timestamp + _ownershipHandoverValidFor();
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute and set the handover slot to `expires`.
                        mstore(0x0c, _HANDOVER_SLOT_SEED)
                        mstore(0x00, caller())
                        sstore(keccak256(0x0c, 0x20), expires)
                        // Emit the {OwnershipHandoverRequested} event.
                        log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                    }
                }
            }
            /// @dev Cancels the two-step ownership handover to the caller, if any.
            function cancelOwnershipHandover() public payable virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute and set the handover slot to 0.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, caller())
                    sstore(keccak256(0x0c, 0x20), 0)
                    // Emit the {OwnershipHandoverCanceled} event.
                    log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
                }
            }
            /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
            /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
            function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute and set the handover slot to 0.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, pendingOwner)
                    let handoverSlot := keccak256(0x0c, 0x20)
                    // If the handover does not exist, or has expired.
                    if gt(timestamp(), sload(handoverSlot)) {
                        mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                        revert(0x1c, 0x04)
                    }
                    // Set the handover slot to 0.
                    sstore(handoverSlot, 0)
                }
                _setOwner(pendingOwner);
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   PUBLIC READ FUNCTIONS                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the owner of the contract.
            function owner() public view virtual returns (address result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := sload(_OWNER_SLOT)
                }
            }
            /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
            function ownershipHandoverExpiresAt(address pendingOwner)
                public
                view
                virtual
                returns (uint256 result)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the handover slot.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, pendingOwner)
                    // Load the handover slot.
                    result := sload(keccak256(0x0c, 0x20))
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         MODIFIERS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Marks a function as only callable by the owner.
            modifier onlyOwner() virtual {
                _checkOwner();
                _;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        import {Ownable} from "./Ownable.sol";
        /// @notice Simple single owner and multiroles authorization mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
        /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
        /// for compatibility, the nomenclature for the 2-step ownership handover and roles
        /// may be unique to this codebase.
        abstract contract OwnableRoles is Ownable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The `user`'s roles is updated to `roles`.
            /// Each bit of `roles` represents whether the role is set.
            event RolesUpdated(address indexed user, uint256 indexed roles);
            /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
            uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
                0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The role slot of `user` is given by:
            /// ```
            ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
            ///     let roleSlot := keccak256(0x00, 0x20)
            /// ```
            /// This automatically ignores the upper bits of the `user` in case
            /// they are not clean, as well as keep the `keccak256` under 32-bytes.
            ///
            /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
            uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     INTERNAL FUNCTIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Overwrite the roles directly without authorization guard.
            function _setRoles(address user, uint256 roles) internal virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    // Store the new value.
                    sstore(keccak256(0x0c, 0x20), roles)
                    // Emit the {RolesUpdated} event.
                    log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
                }
            }
            /// @dev Updates the roles directly without authorization guard.
            /// If `on` is true, each set bit of `roles` will be turned on,
            /// otherwise, each set bit of `roles` will be turned off.
            function _updateRoles(address user, uint256 roles, bool on) internal virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    let roleSlot := keccak256(0x0c, 0x20)
                    // Load the current value.
                    let current := sload(roleSlot)
                    // Compute the updated roles if `on` is true.
                    let updated := or(current, roles)
                    // Compute the updated roles if `on` is false.
                    // Use `and` to compute the intersection of `current` and `roles`,
                    // `xor` it with `current` to flip the bits in the intersection.
                    if iszero(on) { updated := xor(current, and(current, roles)) }
                    // Then, store the new value.
                    sstore(roleSlot, updated)
                    // Emit the {RolesUpdated} event.
                    log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
                }
            }
            /// @dev Grants the roles directly without authorization guard.
            /// Each bit of `roles` represents the role to turn on.
            function _grantRoles(address user, uint256 roles) internal virtual {
                _updateRoles(user, roles, true);
            }
            /// @dev Removes the roles directly without authorization guard.
            /// Each bit of `roles` represents the role to turn off.
            function _removeRoles(address user, uint256 roles) internal virtual {
                _updateRoles(user, roles, false);
            }
            /// @dev Throws if the sender does not have any of the `roles`.
            function _checkRoles(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, caller())
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Throws if the sender is not the owner,
            /// and does not have any of the `roles`.
            /// Checks for ownership first, then lazily checks for roles.
            function _checkOwnerOrRoles(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // If the caller is not the stored owner.
                    // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                    if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                        // Compute the role slot.
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, caller())
                        // Load the stored value, and if the `and` intersection
                        // of the value and `roles` is zero, revert.
                        if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            /// @dev Throws if the sender does not have any of the `roles`,
            /// and is not the owner.
            /// Checks for roles first, then lazily checks for ownership.
            function _checkRolesOrOwner(uint256 roles) internal view virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, caller())
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                        // If the caller is not the stored owner.
                        // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                        if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
            /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
            /// Not recommended to be called on-chain.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
                /// @solidity memory-safe-assembly
                assembly {
                    for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                        // We don't need to mask the values of `ordinals`, as Solidity
                        // cleans dirty upper bits when storing variables into memory.
                        roles := or(shl(mload(add(ordinals, i)), 1), roles)
                    }
                }
            }
            /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
            /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
            /// Not recommended to be called on-chain.
            /// Made internal to conserve bytecode. Wrap it in a public function if needed.
            function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Grab the pointer to the free memory.
                    ordinals := mload(0x40)
                    let ptr := add(ordinals, 0x20)
                    let o := 0
                    // The absence of lookup tables, De Bruijn, etc., here is intentional for
                    // smaller bytecode, as this function is not meant to be called on-chain.
                    for { let t := roles } 1 {} {
                        mstore(ptr, o)
                        // `shr` 5 is equivalent to multiplying by 0x20.
                        // Push back into the ordinals array if the bit is set.
                        ptr := add(ptr, shl(5, and(t, 1)))
                        o := add(o, 1)
                        t := shr(o, roles)
                        if iszero(t) { break }
                    }
                    // Store the length of `ordinals`.
                    mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
                    // Allocate the memory.
                    mstore(0x40, ptr)
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  PUBLIC UPDATE FUNCTIONS                   */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Allows the owner to grant `user` `roles`.
            /// If the `user` already has a role, then it will be an no-op for the role.
            function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
                _grantRoles(user, roles);
            }
            /// @dev Allows the owner to remove `user` `roles`.
            /// If the `user` does not have a role, then it will be an no-op for the role.
            function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
                _removeRoles(user, roles);
            }
            /// @dev Allow the caller to remove their own roles.
            /// If the caller does not have a role, then it will be an no-op for the role.
            function renounceRoles(uint256 roles) public payable virtual {
                _removeRoles(msg.sender, roles);
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   PUBLIC READ FUNCTIONS                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the roles of `user`.
            function rolesOf(address user) public view virtual returns (uint256 roles) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, user)
                    // Load the stored value.
                    roles := sload(keccak256(0x0c, 0x20))
                }
            }
            /// @dev Returns whether `user` has any of `roles`.
            function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
                return rolesOf(user) & roles != 0;
            }
            /// @dev Returns whether `user` has all of `roles`.
            function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
                return rolesOf(user) & roles == roles;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         MODIFIERS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Marks a function as only callable by an account with `roles`.
            modifier onlyRoles(uint256 roles) virtual {
                _checkRoles(roles);
                _;
            }
            /// @dev Marks a function as only callable by the owner or by an account
            /// with `roles`. Checks for ownership first, then lazily checks for roles.
            modifier onlyOwnerOrRoles(uint256 roles) virtual {
                _checkOwnerOrRoles(roles);
                _;
            }
            /// @dev Marks a function as only callable by an account with `roles`
            /// or the owner. Checks for roles first, then lazily checks for ownership.
            modifier onlyRolesOrOwner(uint256 roles) virtual {
                _checkRolesOrOwner(roles);
                _;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       ROLE CONSTANTS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            // IYKYK
            uint256 internal constant _ROLE_0 = 1 << 0;
            uint256 internal constant _ROLE_1 = 1 << 1;
            uint256 internal constant _ROLE_2 = 1 << 2;
            uint256 internal constant _ROLE_3 = 1 << 3;
            uint256 internal constant _ROLE_4 = 1 << 4;
            uint256 internal constant _ROLE_5 = 1 << 5;
            uint256 internal constant _ROLE_6 = 1 << 6;
            uint256 internal constant _ROLE_7 = 1 << 7;
            uint256 internal constant _ROLE_8 = 1 << 8;
            uint256 internal constant _ROLE_9 = 1 << 9;
            uint256 internal constant _ROLE_10 = 1 << 10;
            uint256 internal constant _ROLE_11 = 1 << 11;
            uint256 internal constant _ROLE_12 = 1 << 12;
            uint256 internal constant _ROLE_13 = 1 << 13;
            uint256 internal constant _ROLE_14 = 1 << 14;
            uint256 internal constant _ROLE_15 = 1 << 15;
            uint256 internal constant _ROLE_16 = 1 << 16;
            uint256 internal constant _ROLE_17 = 1 << 17;
            uint256 internal constant _ROLE_18 = 1 << 18;
            uint256 internal constant _ROLE_19 = 1 << 19;
            uint256 internal constant _ROLE_20 = 1 << 20;
            uint256 internal constant _ROLE_21 = 1 << 21;
            uint256 internal constant _ROLE_22 = 1 << 22;
            uint256 internal constant _ROLE_23 = 1 << 23;
            uint256 internal constant _ROLE_24 = 1 << 24;
            uint256 internal constant _ROLE_25 = 1 << 25;
            uint256 internal constant _ROLE_26 = 1 << 26;
            uint256 internal constant _ROLE_27 = 1 << 27;
            uint256 internal constant _ROLE_28 = 1 << 28;
            uint256 internal constant _ROLE_29 = 1 << 29;
            uint256 internal constant _ROLE_30 = 1 << 30;
            uint256 internal constant _ROLE_31 = 1 << 31;
            uint256 internal constant _ROLE_32 = 1 << 32;
            uint256 internal constant _ROLE_33 = 1 << 33;
            uint256 internal constant _ROLE_34 = 1 << 34;
            uint256 internal constant _ROLE_35 = 1 << 35;
            uint256 internal constant _ROLE_36 = 1 << 36;
            uint256 internal constant _ROLE_37 = 1 << 37;
            uint256 internal constant _ROLE_38 = 1 << 38;
            uint256 internal constant _ROLE_39 = 1 << 39;
            uint256 internal constant _ROLE_40 = 1 << 40;
            uint256 internal constant _ROLE_41 = 1 << 41;
            uint256 internal constant _ROLE_42 = 1 << 42;
            uint256 internal constant _ROLE_43 = 1 << 43;
            uint256 internal constant _ROLE_44 = 1 << 44;
            uint256 internal constant _ROLE_45 = 1 << 45;
            uint256 internal constant _ROLE_46 = 1 << 46;
            uint256 internal constant _ROLE_47 = 1 << 47;
            uint256 internal constant _ROLE_48 = 1 << 48;
            uint256 internal constant _ROLE_49 = 1 << 49;
            uint256 internal constant _ROLE_50 = 1 << 50;
            uint256 internal constant _ROLE_51 = 1 << 51;
            uint256 internal constant _ROLE_52 = 1 << 52;
            uint256 internal constant _ROLE_53 = 1 << 53;
            uint256 internal constant _ROLE_54 = 1 << 54;
            uint256 internal constant _ROLE_55 = 1 << 55;
            uint256 internal constant _ROLE_56 = 1 << 56;
            uint256 internal constant _ROLE_57 = 1 << 57;
            uint256 internal constant _ROLE_58 = 1 << 58;
            uint256 internal constant _ROLE_59 = 1 << 59;
            uint256 internal constant _ROLE_60 = 1 << 60;
            uint256 internal constant _ROLE_61 = 1 << 61;
            uint256 internal constant _ROLE_62 = 1 << 62;
            uint256 internal constant _ROLE_63 = 1 << 63;
            uint256 internal constant _ROLE_64 = 1 << 64;
            uint256 internal constant _ROLE_65 = 1 << 65;
            uint256 internal constant _ROLE_66 = 1 << 66;
            uint256 internal constant _ROLE_67 = 1 << 67;
            uint256 internal constant _ROLE_68 = 1 << 68;
            uint256 internal constant _ROLE_69 = 1 << 69;
            uint256 internal constant _ROLE_70 = 1 << 70;
            uint256 internal constant _ROLE_71 = 1 << 71;
            uint256 internal constant _ROLE_72 = 1 << 72;
            uint256 internal constant _ROLE_73 = 1 << 73;
            uint256 internal constant _ROLE_74 = 1 << 74;
            uint256 internal constant _ROLE_75 = 1 << 75;
            uint256 internal constant _ROLE_76 = 1 << 76;
            uint256 internal constant _ROLE_77 = 1 << 77;
            uint256 internal constant _ROLE_78 = 1 << 78;
            uint256 internal constant _ROLE_79 = 1 << 79;
            uint256 internal constant _ROLE_80 = 1 << 80;
            uint256 internal constant _ROLE_81 = 1 << 81;
            uint256 internal constant _ROLE_82 = 1 << 82;
            uint256 internal constant _ROLE_83 = 1 << 83;
            uint256 internal constant _ROLE_84 = 1 << 84;
            uint256 internal constant _ROLE_85 = 1 << 85;
            uint256 internal constant _ROLE_86 = 1 << 86;
            uint256 internal constant _ROLE_87 = 1 << 87;
            uint256 internal constant _ROLE_88 = 1 << 88;
            uint256 internal constant _ROLE_89 = 1 << 89;
            uint256 internal constant _ROLE_90 = 1 << 90;
            uint256 internal constant _ROLE_91 = 1 << 91;
            uint256 internal constant _ROLE_92 = 1 << 92;
            uint256 internal constant _ROLE_93 = 1 << 93;
            uint256 internal constant _ROLE_94 = 1 << 94;
            uint256 internal constant _ROLE_95 = 1 << 95;
            uint256 internal constant _ROLE_96 = 1 << 96;
            uint256 internal constant _ROLE_97 = 1 << 97;
            uint256 internal constant _ROLE_98 = 1 << 98;
            uint256 internal constant _ROLE_99 = 1 << 99;
            uint256 internal constant _ROLE_100 = 1 << 100;
            uint256 internal constant _ROLE_101 = 1 << 101;
            uint256 internal constant _ROLE_102 = 1 << 102;
            uint256 internal constant _ROLE_103 = 1 << 103;
            uint256 internal constant _ROLE_104 = 1 << 104;
            uint256 internal constant _ROLE_105 = 1 << 105;
            uint256 internal constant _ROLE_106 = 1 << 106;
            uint256 internal constant _ROLE_107 = 1 << 107;
            uint256 internal constant _ROLE_108 = 1 << 108;
            uint256 internal constant _ROLE_109 = 1 << 109;
            uint256 internal constant _ROLE_110 = 1 << 110;
            uint256 internal constant _ROLE_111 = 1 << 111;
            uint256 internal constant _ROLE_112 = 1 << 112;
            uint256 internal constant _ROLE_113 = 1 << 113;
            uint256 internal constant _ROLE_114 = 1 << 114;
            uint256 internal constant _ROLE_115 = 1 << 115;
            uint256 internal constant _ROLE_116 = 1 << 116;
            uint256 internal constant _ROLE_117 = 1 << 117;
            uint256 internal constant _ROLE_118 = 1 << 118;
            uint256 internal constant _ROLE_119 = 1 << 119;
            uint256 internal constant _ROLE_120 = 1 << 120;
            uint256 internal constant _ROLE_121 = 1 << 121;
            uint256 internal constant _ROLE_122 = 1 << 122;
            uint256 internal constant _ROLE_123 = 1 << 123;
            uint256 internal constant _ROLE_124 = 1 << 124;
            uint256 internal constant _ROLE_125 = 1 << 125;
            uint256 internal constant _ROLE_126 = 1 << 126;
            uint256 internal constant _ROLE_127 = 1 << 127;
            uint256 internal constant _ROLE_128 = 1 << 128;
            uint256 internal constant _ROLE_129 = 1 << 129;
            uint256 internal constant _ROLE_130 = 1 << 130;
            uint256 internal constant _ROLE_131 = 1 << 131;
            uint256 internal constant _ROLE_132 = 1 << 132;
            uint256 internal constant _ROLE_133 = 1 << 133;
            uint256 internal constant _ROLE_134 = 1 << 134;
            uint256 internal constant _ROLE_135 = 1 << 135;
            uint256 internal constant _ROLE_136 = 1 << 136;
            uint256 internal constant _ROLE_137 = 1 << 137;
            uint256 internal constant _ROLE_138 = 1 << 138;
            uint256 internal constant _ROLE_139 = 1 << 139;
            uint256 internal constant _ROLE_140 = 1 << 140;
            uint256 internal constant _ROLE_141 = 1 << 141;
            uint256 internal constant _ROLE_142 = 1 << 142;
            uint256 internal constant _ROLE_143 = 1 << 143;
            uint256 internal constant _ROLE_144 = 1 << 144;
            uint256 internal constant _ROLE_145 = 1 << 145;
            uint256 internal constant _ROLE_146 = 1 << 146;
            uint256 internal constant _ROLE_147 = 1 << 147;
            uint256 internal constant _ROLE_148 = 1 << 148;
            uint256 internal constant _ROLE_149 = 1 << 149;
            uint256 internal constant _ROLE_150 = 1 << 150;
            uint256 internal constant _ROLE_151 = 1 << 151;
            uint256 internal constant _ROLE_152 = 1 << 152;
            uint256 internal constant _ROLE_153 = 1 << 153;
            uint256 internal constant _ROLE_154 = 1 << 154;
            uint256 internal constant _ROLE_155 = 1 << 155;
            uint256 internal constant _ROLE_156 = 1 << 156;
            uint256 internal constant _ROLE_157 = 1 << 157;
            uint256 internal constant _ROLE_158 = 1 << 158;
            uint256 internal constant _ROLE_159 = 1 << 159;
            uint256 internal constant _ROLE_160 = 1 << 160;
            uint256 internal constant _ROLE_161 = 1 << 161;
            uint256 internal constant _ROLE_162 = 1 << 162;
            uint256 internal constant _ROLE_163 = 1 << 163;
            uint256 internal constant _ROLE_164 = 1 << 164;
            uint256 internal constant _ROLE_165 = 1 << 165;
            uint256 internal constant _ROLE_166 = 1 << 166;
            uint256 internal constant _ROLE_167 = 1 << 167;
            uint256 internal constant _ROLE_168 = 1 << 168;
            uint256 internal constant _ROLE_169 = 1 << 169;
            uint256 internal constant _ROLE_170 = 1 << 170;
            uint256 internal constant _ROLE_171 = 1 << 171;
            uint256 internal constant _ROLE_172 = 1 << 172;
            uint256 internal constant _ROLE_173 = 1 << 173;
            uint256 internal constant _ROLE_174 = 1 << 174;
            uint256 internal constant _ROLE_175 = 1 << 175;
            uint256 internal constant _ROLE_176 = 1 << 176;
            uint256 internal constant _ROLE_177 = 1 << 177;
            uint256 internal constant _ROLE_178 = 1 << 178;
            uint256 internal constant _ROLE_179 = 1 << 179;
            uint256 internal constant _ROLE_180 = 1 << 180;
            uint256 internal constant _ROLE_181 = 1 << 181;
            uint256 internal constant _ROLE_182 = 1 << 182;
            uint256 internal constant _ROLE_183 = 1 << 183;
            uint256 internal constant _ROLE_184 = 1 << 184;
            uint256 internal constant _ROLE_185 = 1 << 185;
            uint256 internal constant _ROLE_186 = 1 << 186;
            uint256 internal constant _ROLE_187 = 1 << 187;
            uint256 internal constant _ROLE_188 = 1 << 188;
            uint256 internal constant _ROLE_189 = 1 << 189;
            uint256 internal constant _ROLE_190 = 1 << 190;
            uint256 internal constant _ROLE_191 = 1 << 191;
            uint256 internal constant _ROLE_192 = 1 << 192;
            uint256 internal constant _ROLE_193 = 1 << 193;
            uint256 internal constant _ROLE_194 = 1 << 194;
            uint256 internal constant _ROLE_195 = 1 << 195;
            uint256 internal constant _ROLE_196 = 1 << 196;
            uint256 internal constant _ROLE_197 = 1 << 197;
            uint256 internal constant _ROLE_198 = 1 << 198;
            uint256 internal constant _ROLE_199 = 1 << 199;
            uint256 internal constant _ROLE_200 = 1 << 200;
            uint256 internal constant _ROLE_201 = 1 << 201;
            uint256 internal constant _ROLE_202 = 1 << 202;
            uint256 internal constant _ROLE_203 = 1 << 203;
            uint256 internal constant _ROLE_204 = 1 << 204;
            uint256 internal constant _ROLE_205 = 1 << 205;
            uint256 internal constant _ROLE_206 = 1 << 206;
            uint256 internal constant _ROLE_207 = 1 << 207;
            uint256 internal constant _ROLE_208 = 1 << 208;
            uint256 internal constant _ROLE_209 = 1 << 209;
            uint256 internal constant _ROLE_210 = 1 << 210;
            uint256 internal constant _ROLE_211 = 1 << 211;
            uint256 internal constant _ROLE_212 = 1 << 212;
            uint256 internal constant _ROLE_213 = 1 << 213;
            uint256 internal constant _ROLE_214 = 1 << 214;
            uint256 internal constant _ROLE_215 = 1 << 215;
            uint256 internal constant _ROLE_216 = 1 << 216;
            uint256 internal constant _ROLE_217 = 1 << 217;
            uint256 internal constant _ROLE_218 = 1 << 218;
            uint256 internal constant _ROLE_219 = 1 << 219;
            uint256 internal constant _ROLE_220 = 1 << 220;
            uint256 internal constant _ROLE_221 = 1 << 221;
            uint256 internal constant _ROLE_222 = 1 << 222;
            uint256 internal constant _ROLE_223 = 1 << 223;
            uint256 internal constant _ROLE_224 = 1 << 224;
            uint256 internal constant _ROLE_225 = 1 << 225;
            uint256 internal constant _ROLE_226 = 1 << 226;
            uint256 internal constant _ROLE_227 = 1 << 227;
            uint256 internal constant _ROLE_228 = 1 << 228;
            uint256 internal constant _ROLE_229 = 1 << 229;
            uint256 internal constant _ROLE_230 = 1 << 230;
            uint256 internal constant _ROLE_231 = 1 << 231;
            uint256 internal constant _ROLE_232 = 1 << 232;
            uint256 internal constant _ROLE_233 = 1 << 233;
            uint256 internal constant _ROLE_234 = 1 << 234;
            uint256 internal constant _ROLE_235 = 1 << 235;
            uint256 internal constant _ROLE_236 = 1 << 236;
            uint256 internal constant _ROLE_237 = 1 << 237;
            uint256 internal constant _ROLE_238 = 1 << 238;
            uint256 internal constant _ROLE_239 = 1 << 239;
            uint256 internal constant _ROLE_240 = 1 << 240;
            uint256 internal constant _ROLE_241 = 1 << 241;
            uint256 internal constant _ROLE_242 = 1 << 242;
            uint256 internal constant _ROLE_243 = 1 << 243;
            uint256 internal constant _ROLE_244 = 1 << 244;
            uint256 internal constant _ROLE_245 = 1 << 245;
            uint256 internal constant _ROLE_246 = 1 << 246;
            uint256 internal constant _ROLE_247 = 1 << 247;
            uint256 internal constant _ROLE_248 = 1 << 248;
            uint256 internal constant _ROLE_249 = 1 << 249;
            uint256 internal constant _ROLE_250 = 1 << 250;
            uint256 internal constant _ROLE_251 = 1 << 251;
            uint256 internal constant _ROLE_252 = 1 << 252;
            uint256 internal constant _ROLE_253 = 1 << 253;
            uint256 internal constant _ROLE_254 = 1 << 254;
            uint256 internal constant _ROLE_255 = 1 << 255;
        }
        // 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 use signatures as unique identifiers:
        /// - 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.
        /// This implementation does NOT check if a signature is non-malleable.
        library ECDSA {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                        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://eth.wiki/json-rpc/API#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:\
        32") // 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://eth.wiki/json-rpc/API#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:\
        ") // 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.
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   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 Contract for EIP-712 typed structured data hashing and signing.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
        /// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
        /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
        ///
        /// @dev Note, this implementation:
        /// - Uses `address(this)` for the `verifyingContract` field.
        /// - Does NOT use the optional EIP-712 salt.
        /// - Does NOT use any EIP-712 extensions.
        /// This is for simplicity and to save gas.
        /// If you need to customize, please fork / modify accordingly.
        abstract contract EIP712 {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  CONSTANTS AND IMMUTABLES                  */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
            bytes32 internal constant _DOMAIN_TYPEHASH =
                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
            uint256 private immutable _cachedThis;
            uint256 private immutable _cachedChainId;
            bytes32 private immutable _cachedNameHash;
            bytes32 private immutable _cachedVersionHash;
            bytes32 private immutable _cachedDomainSeparator;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                        CONSTRUCTOR                         */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Cache the hashes for cheaper runtime gas costs.
            /// In the case of upgradeable contracts (i.e. proxies),
            /// or if the chain id changes due to a hard fork,
            /// the domain separator will be seamlessly calculated on-the-fly.
            constructor() {
                _cachedThis = uint256(uint160(address(this)));
                _cachedChainId = block.chainid;
                string memory name;
                string memory version;
                if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
                bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
                bytes32 versionHash =
                    _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
                _cachedNameHash = nameHash;
                _cachedVersionHash = versionHash;
                bytes32 separator;
                if (!_domainNameAndVersionMayChange()) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Load the free memory pointer.
                        mstore(m, _DOMAIN_TYPEHASH)
                        mstore(add(m, 0x20), nameHash)
                        mstore(add(m, 0x40), versionHash)
                        mstore(add(m, 0x60), chainid())
                        mstore(add(m, 0x80), address())
                        separator := keccak256(m, 0xa0)
                    }
                }
                _cachedDomainSeparator = separator;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   FUNCTIONS TO OVERRIDE                    */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Please override this function to return the domain name and version.
            /// ```
            ///     function _domainNameAndVersion()
            ///         internal
            ///         pure
            ///         virtual
            ///         returns (string memory name, string memory version)
            ///     {
            ///         name = "Solady";
            ///         version = "1";
            ///     }
            /// ```
            ///
            /// Note: If the returned result may change after the contract has been deployed,
            /// you must override `_domainNameAndVersionMayChange()` to return true.
            function _domainNameAndVersion()
                internal
                view
                virtual
                returns (string memory name, string memory version);
            /// @dev Returns if `_domainNameAndVersion()` may change
            /// after the contract has been deployed (i.e. after the constructor).
            /// Default: false.
            function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     HASHING OPERATIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the EIP-712 domain separator.
            function _domainSeparator() internal view virtual returns (bytes32 separator) {
                if (_domainNameAndVersionMayChange()) {
                    separator = _buildDomainSeparator();
                } else {
                    separator = _cachedDomainSeparator;
                    if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
                }
            }
            /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
            /// given `structHash`, as defined in
            /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
            ///
            /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
            /// ```
            ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
            ///         keccak256("Mail(address to,string contents)"),
            ///         mailTo,
            ///         keccak256(bytes(mailContents))
            ///     )));
            ///     address signer = ECDSA.recover(digest, signature);
            /// ```
            function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
                // We will use `digest` to store the domain separator to save a bit of gas.
                if (_domainNameAndVersionMayChange()) {
                    digest = _buildDomainSeparator();
                } else {
                    digest = _cachedDomainSeparator;
                    if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
                }
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute the digest.
                    mstore(0x00, 0x1901000000000000) // Store "\\x19\\x01".
                    mstore(0x1a, digest) // Store the domain separator.
                    mstore(0x3a, structHash) // Store the struct hash.
                    digest := keccak256(0x18, 0x42)
                    // Restore the part of the free memory slot that was overwritten.
                    mstore(0x3a, 0)
                }
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                    EIP-5267 OPERATIONS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
            function eip712Domain()
                public
                view
                virtual
                returns (
                    bytes1 fields,
                    string memory name,
                    string memory version,
                    uint256 chainId,
                    address verifyingContract,
                    bytes32 salt,
                    uint256[] memory extensions
                )
            {
                fields = hex"0f"; // `0b01111`.
                (name, version) = _domainNameAndVersion();
                chainId = block.chainid;
                verifyingContract = address(this);
                salt = salt; // `bytes32(0)`.
                extensions = extensions; // `new uint256[](0)`.
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      PRIVATE HELPERS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the EIP-712 domain separator.
            function _buildDomainSeparator() private view returns (bytes32 separator) {
                // We will use `separator` to store the name hash to save a bit of gas.
                bytes32 versionHash;
                if (_domainNameAndVersionMayChange()) {
                    (string memory name, string memory version) = _domainNameAndVersion();
                    separator = keccak256(bytes(name));
                    versionHash = keccak256(bytes(version));
                } else {
                    separator = _cachedNameHash;
                    versionHash = _cachedVersionHash;
                }
                /// @solidity memory-safe-assembly
                assembly {
                    let m := mload(0x40) // Load the free memory pointer.
                    mstore(m, _DOMAIN_TYPEHASH)
                    mstore(add(m, 0x20), separator) // Name hash.
                    mstore(add(m, 0x40), versionHash)
                    mstore(add(m, 0x60), chainid())
                    mstore(add(m, 0x80), address())
                    separator := keccak256(m, 0xa0)
                }
            }
            /// @dev Returns if the cached domain separator has been invalidated.
            function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
                uint256 cachedChainId = _cachedChainId;
                uint256 cachedThis = _cachedThis;
                /// @solidity memory-safe-assembly
                assembly {
                    result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Library for managing enumerable sets in storage.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibMap.sol)
        ///
        /// @dev Note:
        /// In many applications, the number of elements in an enumerable set is small.
        /// This enumerable set implementation avoids storing the length and indices
        /// for up to 3 elements. Once the length exceeds 3 for the first time, the length
        /// and indices will be initialized. The amortized cost of adding elements is O(1).
        ///
        /// The AddressSet implementation packs the length with the 0th entry.
        library EnumerableSetLib {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The index must be less than the length.
            error IndexOutOfBounds();
            /// @dev The value cannot be the zero sentinel.
            error ValueIsZeroSentinel();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         CONSTANTS                          */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev A sentinel value to denote the zero value in storage.
            /// No elements can be equal to this value.
            /// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
            uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;
            /// @dev The storage layout is given by:
            /// ```
            ///     mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
            ///     mstore(0x00, set.slot)
            ///     let rootSlot := keccak256(0x00, 0x24)
            ///     mstore(0x20, rootSlot)
            ///     mstore(0x00, shr(96, shl(96, value)))
            ///     let positionSlot := keccak256(0x00, 0x40)
            ///     let valueSlot := add(rootSlot, sload(positionSlot))
            ///     let valueInStorage := shr(96, sload(valueSlot))
            ///     let lazyLength := shr(160, shl(160, sload(rootSlot)))
            /// ```
            uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;
            /// @dev The storage layout is given by:
            /// ```
            ///     mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
            ///     mstore(0x00, set.slot)
            ///     let rootSlot := keccak256(0x00, 0x24)
            ///     mstore(0x20, rootSlot)
            ///     mstore(0x00, value)
            ///     let positionSlot := keccak256(0x00, 0x40)
            ///     let valueSlot := add(rootSlot, sload(positionSlot))
            ///     let valueInStorage := sload(valueSlot)
            ///     let lazyLength := sload(not(rootSlot))
            /// ```
            uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STRUCTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev An enumerable address set in storage.
            struct AddressSet {
                uint256 _spacer;
            }
            /// @dev An enumerable bytes32 set in storage.
            struct Bytes32Set {
                uint256 _spacer;
            }
            /// @dev An enumerable uint256 set in storage.
            struct Uint256Set {
                uint256 _spacer;
            }
            /// @dev An enumerable int256 set in storage.
            struct Int256Set {
                uint256 _spacer;
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                     GETTERS / SETTERS                      */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the number of elements in the set.
            function length(AddressSet storage set) internal view returns (uint256 result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let rootPacked := sload(rootSlot)
                    let n := shr(160, shl(160, rootPacked))
                    result := shr(1, n)
                    for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
                        result := 1
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 2
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 3
                        break
                    }
                }
            }
            /// @dev Returns the number of elements in the set.
            function length(Bytes32Set storage set) internal view returns (uint256 result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let n := sload(not(rootSlot))
                    result := shr(1, n)
                    for {} iszero(n) {} {
                        result := 0
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 1
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 2
                        if iszero(sload(add(rootSlot, result))) { break }
                        result := 3
                        break
                    }
                }
            }
            /// @dev Returns the number of elements in the set.
            function length(Uint256Set storage set) internal view returns (uint256 result) {
                result = length(_toBytes32Set(set));
            }
            /// @dev Returns the number of elements in the set.
            function length(Int256Set storage set) internal view returns (uint256 result) {
                result = length(_toBytes32Set(set));
            }
            /// @dev Returns whether `value` is in the set.
            function contains(AddressSet storage set, address value) internal view returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for {} 1 {} {
                        if iszero(shr(160, shl(160, rootPacked))) {
                            result := 1
                            if eq(shr(96, rootPacked), value) { break }
                            if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
                            if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                        break
                    }
                }
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for {} 1 {} {
                        if iszero(sload(not(rootSlot))) {
                            result := 1
                            if eq(sload(rootSlot), value) { break }
                            if eq(sload(add(rootSlot, 1)), value) { break }
                            if eq(sload(add(rootSlot, 2)), value) { break }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                        break
                    }
                }
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
                result = contains(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Returns whether `value` is in the set.
            function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
                result = contains(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(AddressSet storage set, address value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                        mstore(0x20, rootSlot)
                        if iszero(n) {
                            let v0 := shr(96, rootPacked)
                            if iszero(v0) {
                                sstore(rootSlot, shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v0, value) { break }
                            let v1 := shr(96, sload(add(rootSlot, 1)))
                            if iszero(v1) {
                                sstore(add(rootSlot, 1), shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v1, value) { break }
                            let v2 := shr(96, sload(add(rootSlot, 2)))
                            if iszero(v2) {
                                sstore(add(rootSlot, 2), shl(96, value))
                                result := 1
                                break
                            }
                            if eq(v2, value) { break }
                            mstore(0x00, v0)
                            sstore(keccak256(0x00, 0x40), 1)
                            mstore(0x00, v1)
                            sstore(keccak256(0x00, 0x40), 2)
                            mstore(0x00, v2)
                            sstore(keccak256(0x00, 0x40), 3)
                            rootPacked := or(rootPacked, 7)
                            n := 7
                        }
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        if iszero(sload(p)) {
                            n := shr(1, n)
                            sstore(add(rootSlot, n), shl(96, value))
                            sstore(p, add(1, n))
                            sstore(rootSlot, add(2, rootPacked))
                            result := 1
                            break
                        }
                        break
                    }
                }
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for { let n := sload(not(rootSlot)) } 1 {} {
                        mstore(0x20, rootSlot)
                        if iszero(n) {
                            let v0 := sload(rootSlot)
                            if iszero(v0) {
                                sstore(rootSlot, value)
                                result := 1
                                break
                            }
                            if eq(v0, value) { break }
                            let v1 := sload(add(rootSlot, 1))
                            if iszero(v1) {
                                sstore(add(rootSlot, 1), value)
                                result := 1
                                break
                            }
                            if eq(v1, value) { break }
                            let v2 := sload(add(rootSlot, 2))
                            if iszero(v2) {
                                sstore(add(rootSlot, 2), value)
                                result := 1
                                break
                            }
                            if eq(v2, value) { break }
                            mstore(0x00, v0)
                            sstore(keccak256(0x00, 0x40), 1)
                            mstore(0x00, v1)
                            sstore(keccak256(0x00, 0x40), 2)
                            mstore(0x00, v2)
                            sstore(keccak256(0x00, 0x40), 3)
                            n := 7
                        }
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        if iszero(sload(p)) {
                            n := shr(1, n)
                            sstore(add(rootSlot, n), value)
                            sstore(p, add(1, n))
                            sstore(not(rootSlot), or(1, shl(1, add(1, n))))
                            result := 1
                            break
                        }
                        break
                    }
                }
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
                result = add(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
            function add(Int256Set storage set, int256 value) internal returns (bool result) {
                result = add(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(AddressSet storage set, address value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    value := shr(96, shl(96, value))
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    let rootPacked := sload(rootSlot)
                    for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                        if iszero(n) {
                            result := 1
                            if eq(shr(96, rootPacked), value) {
                                sstore(rootSlot, sload(add(rootSlot, 1)))
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(shr(96, sload(add(rootSlot, 1))), value) {
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(shr(96, sload(add(rootSlot, 2))), value) {
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        let position := sload(p)
                        if iszero(position) { break }
                        n := sub(shr(1, n), 1)
                        if iszero(eq(sub(position, 1), n)) {
                            let lastValue := shr(96, sload(add(rootSlot, n)))
                            sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
                            sstore(add(rootSlot, n), 0)
                            mstore(0x00, lastValue)
                            sstore(keccak256(0x00, 0x40), position)
                        }
                        sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
                        sstore(p, 0)
                        result := 1
                        break
                    }
                }
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(value, _ZERO_SENTINEL) {
                        mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(value) { value := _ZERO_SENTINEL }
                    for { let n := sload(not(rootSlot)) } 1 {} {
                        if iszero(n) {
                            result := 1
                            if eq(sload(rootSlot), value) {
                                sstore(rootSlot, sload(add(rootSlot, 1)))
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(sload(add(rootSlot, 1)), value) {
                                sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            if eq(sload(add(rootSlot, 2)), value) {
                                sstore(add(rootSlot, 2), 0)
                                break
                            }
                            result := 0
                            break
                        }
                        mstore(0x20, rootSlot)
                        mstore(0x00, value)
                        let p := keccak256(0x00, 0x40)
                        let position := sload(p)
                        if iszero(position) { break }
                        n := sub(shr(1, n), 1)
                        if iszero(eq(sub(position, 1), n)) {
                            let lastValue := sload(add(rootSlot, n))
                            sstore(add(rootSlot, sub(position, 1)), lastValue)
                            sstore(add(rootSlot, n), 0)
                            mstore(0x00, lastValue)
                            sstore(keccak256(0x00, 0x40), position)
                        }
                        sstore(not(rootSlot), or(shl(1, n), 1))
                        sstore(p, 0)
                        result := 1
                        break
                    }
                }
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
                result = remove(_toBytes32Set(set), bytes32(value));
            }
            /// @dev Removes `value` from the set. Returns whether `value` was in the set.
            function remove(Int256Set storage set, int256 value) internal returns (bool result) {
                result = remove(_toBytes32Set(set), bytes32(uint256(value)));
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(AddressSet storage set) internal view returns (address[] memory result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let zs := _ZERO_SENTINEL
                    let rootPacked := sload(rootSlot)
                    let n := shr(160, shl(160, rootPacked))
                    result := mload(0x40)
                    let o := add(0x20, result)
                    let v := shr(96, rootPacked)
                    mstore(o, mul(v, iszero(eq(v, zs))))
                    for {} 1 {} {
                        if iszero(n) {
                            if v {
                                n := 1
                                v := shr(96, sload(add(rootSlot, n)))
                                if v {
                                    n := 2
                                    mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                                    v := shr(96, sload(add(rootSlot, n)))
                                    if v {
                                        n := 3
                                        mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                                    }
                                }
                            }
                            break
                        }
                        n := shr(1, n)
                        for { let i := 1 } lt(i, n) { i := add(i, 1) } {
                            v := shr(96, sload(add(rootSlot, i)))
                            mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                        }
                        break
                    }
                    mstore(result, n)
                    mstore(0x40, add(o, shl(5, n)))
                }
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    let zs := _ZERO_SENTINEL
                    let n := sload(not(rootSlot))
                    result := mload(0x40)
                    let o := add(0x20, result)
                    for {} 1 {} {
                        if iszero(n) {
                            let v := sload(rootSlot)
                            if v {
                                n := 1
                                mstore(o, mul(v, iszero(eq(v, zs))))
                                v := sload(add(rootSlot, n))
                                if v {
                                    n := 2
                                    mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                                    v := sload(add(rootSlot, n))
                                    if v {
                                        n := 3
                                        mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                                    }
                                }
                            }
                            break
                        }
                        n := shr(1, n)
                        for { let i := 0 } lt(i, n) { i := add(i, 1) } {
                            let v := sload(add(rootSlot, i))
                            mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                        }
                        break
                    }
                    mstore(result, n)
                    mstore(0x40, add(o, shl(5, n)))
                }
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
                result = _toUints(values(_toBytes32Set(set)));
            }
            /// @dev Returns all of the values in the set.
            /// Note: This can consume more gas than the block gas limit for large sets.
            function values(Int256Set storage set) internal view returns (int256[] memory result) {
                result = _toInts(values(_toBytes32Set(set)));
            }
            /// @dev Returns the element at index `i` in the set.
            function at(AddressSet storage set, uint256 i) internal view returns (address result) {
                bytes32 rootSlot = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    result := shr(96, sload(add(rootSlot, i)))
                    result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
                }
                if (i >= length(set)) revert IndexOutOfBounds();
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
                result = _rootSlot(set);
                /// @solidity memory-safe-assembly
                assembly {
                    result := sload(add(result, i))
                    result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
                }
                if (i >= length(set)) revert IndexOutOfBounds();
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
                result = uint256(at(_toBytes32Set(set), i));
            }
            /// @dev Returns the element at index `i` in the set.
            function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
                result = int256(uint256(at(_toBytes32Set(set), i)));
            }
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      PRIVATE HELPERS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Returns the root slot.
            function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
                    mstore(0x00, s.slot)
                    r := keccak256(0x00, 0x24)
                }
            }
            /// @dev Returns the root slot.
            function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
                    mstore(0x00, s.slot)
                    r := keccak256(0x00, 0x24)
                }
            }
            /// @dev Casts to a Bytes32Set.
            function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c.slot := s.slot
                }
            }
            /// @dev Casts to a Bytes32Set.
            function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c.slot := s.slot
                }
            }
            /// @dev Casts to a uint256 array.
            function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c := a
                }
            }
            /// @dev Casts to a int256 array.
            function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
                /// @solidity memory-safe-assembly
                assembly {
                    c := a
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Initializable mixin for the upgradeable contracts.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
        /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
        abstract contract Initializable {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The contract is already initialized.
            error InvalidInitialization();
            /// @dev The contract is not initializing.
            error NotInitializing();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                           EVENTS                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Triggered when the contract has been initialized.
            event Initialized(uint64 version);
            /// @dev `keccak256(bytes("Initialized(uint64)"))`.
            bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
                0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev The default initializable slot is given by:
            /// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
            ///
            /// Bits Layout:
            /// - [0]     `initializing`
            /// - [1..64] `initializedVersion`
            bytes32 private constant _INITIALIZABLE_SLOT =
                0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                         OPERATIONS                         */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Override to return a custom storage slot if required.
            function _initializableSlot() internal pure virtual returns (bytes32) {
                return _INITIALIZABLE_SLOT;
            }
            /// @dev Guards an initializer function so that it can be invoked at most once.
            ///
            /// You can guard a function with `onlyInitializing` such that it can be called
            /// through a function guarded with `initializer`.
            ///
            /// This is similar to `reinitializer(1)`, except that in the context of a constructor,
            /// an `initializer` guarded function can be invoked multiple times.
            /// This can be useful during testing and is not expected to be used in production.
            ///
            /// Emits an {Initialized} event.
            modifier initializer() virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    let i := sload(s)
                    // Set `initializing` to 1, `initializedVersion` to 1.
                    sstore(s, 3)
                    // If `!(initializing == 0 && initializedVersion == 0)`.
                    if i {
                        // If `!(address(this).code.length == 0 && initializedVersion == 1)`.
                        if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
                            mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                            revert(0x1c, 0x04)
                        }
                        s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
                    }
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    if s {
                        // Set `initializing` to 0, `initializedVersion` to 1.
                        sstore(s, 2)
                        // Emit the {Initialized} event.
                        mstore(0x20, 1)
                        log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                    }
                }
            }
            /// @dev Guards an reinitialzer function so that it can be invoked at most once.
            ///
            /// You can guard a function with `onlyInitializing` such that it can be called
            /// through a function guarded with `reinitializer`.
            ///
            /// Emits an {Initialized} event.
            modifier reinitializer(uint64 version) virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    version := and(version, 0xffffffffffffffff) // Clean upper bits.
                    let i := sload(s)
                    // If `initializing == 1 || initializedVersion >= version`.
                    if iszero(lt(and(i, 1), lt(shr(1, i), version))) {
                        mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                        revert(0x1c, 0x04)
                    }
                    // Set `initializing` to 1, `initializedVersion` to `version`.
                    sstore(s, or(1, shl(1, version)))
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    // Set `initializing` to 0, `initializedVersion` to `version`.
                    sstore(s, shl(1, version))
                    // Emit the {Initialized} event.
                    mstore(0x20, version)
                    log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                }
            }
            /// @dev Guards a function such that it can only be called in the scope
            /// of a function guarded with `initializer` or `reinitializer`.
            modifier onlyInitializing() virtual {
                _checkInitializing();
                _;
            }
            /// @dev Reverts if the contract is not initializing.
            function _checkInitializing() internal view virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    if iszero(and(1, sload(s))) {
                        mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
            /// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
            ///
            /// Calling this in the constructor will prevent the contract from being initialized
            /// or reinitialized. 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 called.
            function _disableInitializers() internal virtual {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    let i := sload(s)
                    if and(i, 1) {
                        mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                        revert(0x1c, 0x04)
                    }
                    let uint64max := shr(192, s) // Computed to save bytecode.
                    if iszero(eq(shr(1, i), uint64max)) {
                        // Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
                        sstore(s, shl(1, uint64max))
                        // Emit the {Initialized} event.
                        mstore(0x20, uint64max)
                        log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
                    }
                }
            }
            /// @dev Returns the highest version that has been initialized.
            function _getInitializedVersion() internal view virtual returns (uint64 version) {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    version := shr(1, sload(s))
                }
            }
            /// @dev Returns whether the contract is currently initializing.
            function _isInitializing() internal view virtual returns (bool result) {
                bytes32 s = _initializableSlot();
                /// @solidity memory-safe-assembly
                assembly {
                    result := and(1, sload(s))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Contract that enables a single call to call multiple methods on itself.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol)
        /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol)
        ///
        /// WARNING:
        /// This implementation is NOT to be used with ERC2771 out-of-the-box.
        /// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
        /// This also applies to potentially other ERCs / patterns appending to the back of calldata.
        ///
        /// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context.
        /// Moreover, it is infeasible and inefficient for us to add checks and mitigations
        /// for all possible ERC / patterns appending to the back of calldata.
        ///
        /// We would highly recommend using an alternative pattern such as
        /// https://github.com/Vectorized/multicaller
        /// which is more flexible, futureproof, and safer by default.
        abstract contract Multicallable {
            /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`,
            /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`.
            /// If any of the `DELEGATECALL`s reverts, the entire context is reverted,
            /// and the error is bubbled up.
            ///
            /// This function is deliberately made non-payable to guard against double-spending.
            /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong)
            ///
            /// For efficiency, this function will directly return the results, terminating the context.
            /// If called internally, it must be called at the end of a function
            /// that returns `(bytes[] memory)`.
            function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) {
                assembly {
                    mstore(0x00, 0x20)
                    mstore(0x20, data.length) // Store `data.length` into `results`.
                    // Early return if no data.
                    if iszero(data.length) { return(0x00, 0x40) }
                    let results := 0x40
                    // `shl` 5 is equivalent to multiplying by 0x20.
                    let end := shl(5, data.length)
                    // Copy the offsets from calldata into memory.
                    calldatacopy(0x40, data.offset, end)
                    // Offset into `results`.
                    let resultsOffset := end
                    // Pointer to the end of `results`.
                    end := add(results, end)
                    for {} 1 {} {
                        // The offset of the current bytes in the calldata.
                        let o := add(data.offset, mload(results))
                        let m := add(resultsOffset, 0x40)
                        // Copy the current bytes from calldata to the memory.
                        calldatacopy(
                            m,
                            add(o, 0x20), // The offset of the current bytes' bytes.
                            calldataload(o) // The length of the current bytes.
                        )
                        if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) {
                            // Bubble up the revert if the delegatecall reverts.
                            returndatacopy(0x00, 0x00, returndatasize())
                            revert(0x00, returndatasize())
                        }
                        // Append the current `resultsOffset` into `results`.
                        mstore(results, resultsOffset)
                        results := add(results, 0x20)
                        // Append the `returndatasize()`, and the return data.
                        mstore(m, returndatasize())
                        returndatacopy(add(m, 0x20), 0x00, returndatasize())
                        // Advance the `resultsOffset` by `returndatasize() + 0x20`,
                        // rounded up to the next multiple of 32.
                        resultsOffset :=
                            and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0)
                        if iszero(lt(results, end)) { break }
                    }
                    return(0x00, add(resultsOffset, 0x40))
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        /// @notice Reentrancy guard mixin.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
        abstract contract ReentrancyGuard {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       CUSTOM ERRORS                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Unauthorized reentrant call.
            error Reentrancy();
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                          STORAGE                           */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
            /// 9 bytes is large enough to avoid collisions with lower slots,
            /// but not too large to result in excessive bytecode bloat.
            uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      REENTRANCY GUARD                      */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            /// @dev Guards a function from reentrancy.
            modifier nonReentrant() virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                        mstore(0x00, 0xab143c06) // `Reentrancy()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(_REENTRANCY_GUARD_SLOT, address())
                }
                _;
                /// @solidity memory-safe-assembly
                assembly {
                    sstore(_REENTRANCY_GUARD_SLOT, codesize())
                }
            }
            /// @dev Guards a view function from read-only reentrancy.
            modifier nonReadReentrant() virtual {
                /// @solidity memory-safe-assembly
                assembly {
                    if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                        mstore(0x00, 0xab143c06) // `Reentrancy()`.
                        revert(0x1c, 0x04)
                    }
                }
                _;
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        // Interface
        import {ICore} from "./interface/ICore.sol";
        import {IInstallationCallback} from "./interface/IInstallationCallback.sol";
        import {IModule} from "./interface/IModule.sol";
        // Utils
        import {Role} from "./Role.sol";
        import {OwnableRoles} from "@solady/auth/OwnableRoles.sol";
        import {EnumerableSetLib} from "@solady/utils/EnumerableSetLib.sol";
        import {ReentrancyGuard} from "@solady/utils/ReentrancyGuard.sol";
        abstract contract Core is ICore, OwnableRoles, ReentrancyGuard {
            using EnumerableSetLib for EnumerableSetLib.AddressSet;
            /*//////////////////////////////////////////////////////////////
                                        TYPES
            //////////////////////////////////////////////////////////////*/
            /// @dev The type of function callable on module contracts.
            enum FunctionType {
                CALLBACK,
                FALLBACK
            }
            /// @dev Internal representation of a fallback function callable via fallback().
            struct InstalledFunction {
                address implementation;
                uint256 permissionBits;
                FunctionType fnType;
            }
            /*//////////////////////////////////////////////////////////////
                                        EVENTS
            //////////////////////////////////////////////////////////////*/
            /// @notice Emitted when a module is installed.
            event ModuleInstalled(address caller, address implementation, address installedModule);
            /// @notice Emitted when a module is uninstalled.
            event ModuleUninstalled(address caller, address implementation, address installedModule);
            /*//////////////////////////////////////////////////////////////
                                        STORAGE
            //////////////////////////////////////////////////////////////*/
            /// @dev The set of addresses of installed modules.
            EnumerableSetLib.AddressSet private modules;
            /// @dev interface ID => counter of modules supporting the interface.
            mapping(bytes4 => uint256) private supportedInterfaceRefCounter;
            /// @dev function selector => function data.
            mapping(bytes4 => InstalledFunction) private functionData_;
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error ModuleOutOfSync();
            error ModuleNotInstalled();
            error ModuleAlreadyInstalled();
            error CallbackFunctionRequired();
            error CallbackExecutionReverted();
            error CallbackFunctionNotSupported();
            error CallbackFunctionAlreadyInstalled();
            error CallbackFunctionUnauthorizedCall();
            error FallbackFunctionAlreadyInstalled();
            error FallbackFunctionNotInstalled();
            error ModuleInterfaceNotCompatible(bytes4 requiredInterfaceId);
            /*//////////////////////////////////////////////////////////////
                                    FALLBACK FUNCTION
            //////////////////////////////////////////////////////////////*/
            /// @notice Routes a call to the appropriate module contract.
            fallback() external payable {
                // Get module function data.
                InstalledFunction memory fn = functionData_[msg.sig];
                // Check: module function data exists.
                if (fn.implementation == address(0)) {
                    revert FallbackFunctionNotInstalled();
                }
                // Check: authorized to call permissioned module function
                if (fn.fnType == FunctionType.CALLBACK) {
                    if (msg.sender != address(this)) {
                        revert CallbackFunctionUnauthorizedCall();
                    }
                } else if (fn.fnType == FunctionType.FALLBACK && fn.permissionBits > 0) {
                    _checkOwnerOrRoles(fn.permissionBits);
                }
                _delegateAndReturn(fn.implementation);
            }
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Returns the list of all callback functions called on some module contract.
            function getSupportedCallbackFunctions() public pure virtual returns (SupportedCallbackFunction[] memory);
            /// @notice Returns a list of addresess and respective module configs of all installed modules.
            function getInstalledModules() external view returns (InstalledModule[] memory _installedModules) {
                uint256 totalInstalled = modules.length();
                _installedModules = new InstalledModule[](totalInstalled);
                for (uint256 i = 0; i < totalInstalled; i++) {
                    address implementation = modules.at(i);
                    _installedModules[i] =
                        InstalledModule({implementation: implementation, config: IModule(implementation).getModuleConfig()});
                }
            }
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Installs a module contract.
            function installModule(address _module, bytes calldata _data)
                external
                payable
                onlyOwnerOrRoles(Role._INSTALLER_ROLE)
            {
                // Install module.
                _installModule(_module, _data);
            }
            /// @notice Uninstalls a module contract.
            function uninstallModule(address _module, bytes calldata _data)
                external
                payable
                onlyOwnerOrRoles(Role._INSTALLER_ROLE)
            {
                // Uninstall module.
                _uninstallModule(_module, _data);
            }
            /// @notice Returns whether a given interface is implemented by the contract.
            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                if (interfaceId == 0xffffffff) {
                    return false;
                }
                if (interfaceId == 0x01ffc9a7) {
                    // ERC165 Interface ID for ERC165
                    return true;
                }
                if (supportedInterfaceRefCounter[interfaceId] > 0) {
                    return true;
                }
                return false;
            }
            /*//////////////////////////////////////////////////////////////
                                    INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @notice Returns whether a given interface is implemented by the contract.
            function _supportsInterfaceViaModules(bytes4 interfaceId) internal view virtual returns (bool) {
                if (interfaceId == 0xffffffff) {
                    return false;
                }
                if (supportedInterfaceRefCounter[interfaceId] > 0) {
                    return true;
                }
                return false;
            }
            /// @dev Installs a module contract.
            function _installModule(address _module, bytes memory _data) internal {
                if (!modules.add(_module)) {
                    revert ModuleAlreadyInstalled();
                }
                // Get module config.
                ModuleConfig memory config = IModule(_module).getModuleConfig();
                // Check: Core supports interface required by module.
                if (config.requiredInterfaces.length != 0) {
                    for (uint256 i = 0; i < config.requiredInterfaces.length; i++) {
                        if (!supportsInterface(config.requiredInterfaces[i])) {
                            revert ModuleInterfaceNotCompatible(config.requiredInterfaces[i]);
                        }
                    }
                }
                // Store interface support inherited via module installation.
                uint256 supportedInterfaceLength = config.supportedInterfaces.length;
                for (uint256 i = 0; i < supportedInterfaceLength; i++) {
                    supportedInterfaceRefCounter[config.supportedInterfaces[i]] += 1;
                }
                // Store callback function data. Only install supported callback functions
                SupportedCallbackFunction[] memory supportedCallbacks = getSupportedCallbackFunctions();
                uint256 supportedCallbacksLength = supportedCallbacks.length;
                uint256 callbackLength = config.callbackFunctions.length;
                for (uint256 i = 0; i < callbackLength; i++) {
                    CallbackFunction memory callbackFunction = config.callbackFunctions[i];
                    // Check: callback function data not already stored.
                    if (functionData_[callbackFunction.selector].implementation != address(0)) {
                        revert CallbackFunctionAlreadyInstalled();
                    }
                    // Check: callback function is supported
                    bool supported = false;
                    for (uint256 j = 0; j < supportedCallbacksLength; j++) {
                        if (supportedCallbacks[j].selector == callbackFunction.selector) {
                            supported = true;
                            break;
                        }
                    }
                    if (!supported) {
                        revert CallbackFunctionNotSupported();
                    }
                    functionData_[callbackFunction.selector] =
                        InstalledFunction({implementation: _module, permissionBits: 0, fnType: FunctionType.CALLBACK});
                }
                // Store module function data.
                uint256 functionLength = config.fallbackFunctions.length;
                for (uint256 i = 0; i < functionLength; i++) {
                    FallbackFunction memory ext = config.fallbackFunctions[i];
                    // Check: module function data not already stored.
                    if (functionData_[ext.selector].implementation != address(0)) {
                        revert FallbackFunctionAlreadyInstalled();
                    }
                    functionData_[ext.selector] = InstalledFunction({
                        implementation: _module,
                        permissionBits: ext.permissionBits,
                        fnType: FunctionType.FALLBACK
                    });
                }
                // Call `onInstall` callback function if module has registered installation callback.
                if (config.registerInstallationCallback) {
                    (bool success, bytes memory returndata) =
                        _module.delegatecall(abi.encodeCall(IInstallationCallback.onInstall, (_data)));
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                }
                emit ModuleInstalled(msg.sender, _module, _module);
            }
            /// @notice Uninstalls a module contract.
            function _uninstallModule(address _module, bytes memory _data) internal {
                // Check: remove and check if the module is installed
                if (!modules.remove(_module)) {
                    revert ModuleNotInstalled();
                }
                // Get module config.
                ModuleConfig memory config = IModule(_module).getModuleConfig();
                uint256 supportedInterfaceLength = config.supportedInterfaces.length;
                for (uint256 i = 0; i < supportedInterfaceLength; i++) {
                    // Note: This should not underflow because module needs to be installed before uninstalling. getModuleConfig should returns the same value during installation and uninstallation.
                    supportedInterfaceRefCounter[config.supportedInterfaces[i]] -= 1;
                }
                // Remove module function data
                uint256 functionLength = config.fallbackFunctions.length;
                for (uint256 i = 0; i < functionLength; i++) {
                    delete functionData_[config.fallbackFunctions[i].selector];
                }
                // Remove callback function data
                uint256 callbackLength = config.callbackFunctions.length;
                for (uint256 i = 0; i < callbackLength; i++) {
                    delete functionData_[config.callbackFunctions[i].selector];
                }
                if (config.registerInstallationCallback) {
                    _module.delegatecall(abi.encodeCall(IInstallationCallback.onUninstall, (_data)));
                }
                emit ModuleUninstalled(msg.sender, _module, _module);
            }
            /// @dev Calls a module callback function and checks whether it is optional or required.
            function _executeCallbackFunction(bytes4 _selector, bytes memory _abiEncodedCalldata)
                internal
                nonReentrant
                returns (bool success, bytes memory returndata)
            {
                InstalledFunction memory callbackFunction = functionData_[_selector];
                // Verify that the function is a callback function
                if (callbackFunction.fnType != FunctionType.CALLBACK) {
                    revert CallbackFunctionNotSupported();
                }
                if (callbackFunction.implementation != address(0)) {
                    (success, returndata) = callbackFunction.implementation.delegatecall(_abiEncodedCalldata);
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                } else {
                    // Get callback mode -- required or not required.
                    SupportedCallbackFunction[] memory functions = getSupportedCallbackFunctions();
                    uint256 len = functions.length;
                    for (uint256 i = 0; i < len; i++) {
                        if (functions[i].selector == _selector) {
                            if (functions[i].mode == CallbackMode.REQUIRED) {
                                revert CallbackFunctionRequired();
                            }
                            break;
                        }
                    }
                }
            }
            /// @dev Calls a module callback function and checks whether it is optional or required.
            function _executeCallbackFunctionView(bytes4 _selector, bytes memory _abiEncodedCalldata)
                internal
                view
                returns (bool success, bytes memory returndata)
            {
                InstalledFunction memory callbackFunction = functionData_[_selector];
                // Verify that the function is a callback function
                if (callbackFunction.fnType != FunctionType.CALLBACK) {
                    revert CallbackFunctionNotSupported();
                }
                if (callbackFunction.implementation != address(0)) {
                    (success, returndata) = address(this).staticcall(_abiEncodedCalldata);
                    if (!success) {
                        _revert(returndata, CallbackExecutionReverted.selector);
                    }
                } else {
                    // Get callback mode -- required or not required.
                    SupportedCallbackFunction[] memory functions = getSupportedCallbackFunctions();
                    uint256 len = functions.length;
                    for (uint256 i = 0; i < len; i++) {
                        if (functions[i].selector == _selector) {
                            if (functions[i].mode == CallbackMode.REQUIRED) {
                                revert CallbackFunctionRequired();
                            }
                            break;
                        }
                    }
                }
            }
            /// @dev delegateCalls an `implementation` smart contract.
            /// @notice Only use this at the end of the function as it reverts or returns the result
            function _delegateAndReturn(address _implementation) private {
                /// @solidity memory-safe-assembly
                assembly {
                    function allocate(length) -> pos {
                        pos := mload(0x40)
                        mstore(0x40, add(pos, length))
                    }
                    let calldataPtr := allocate(calldatasize())
                    calldatacopy(calldataPtr, 0, calldatasize())
                    let success := delegatecall(gas(), _implementation, calldataPtr, calldatasize(), 0, 0)
                    let returnDataPtr := allocate(returndatasize())
                    returndatacopy(returnDataPtr, 0, returndatasize())
                    if iszero(success) { revert(returnDataPtr, returndatasize()) }
                    return(returnDataPtr, returndatasize())
                }
            }
            /// @dev Reverts with the given return data / error message.
            function _revert(bytes memory _returnData, bytes4 _errorSignature) internal 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 {
                        revert(add(0x20, _returnData), mload(_returnData))
                    }
                } else {
                    assembly {
                        mstore(0x00, _errorSignature)
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        library Role {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                   NAMED ROLE CONSTANTS                     */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            uint256 internal constant _MINTER_ROLE = 1 << 0;
            uint256 internal constant _MANAGER_ROLE = 1 << 1;
            uint256 internal constant _INSTALLER_ROLE = 1 << 255;
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                       ROLE CONSTANTS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
            uint256 internal constant _ROLE_0 = 1 << 0;
            uint256 internal constant _ROLE_1 = 1 << 1;
            uint256 internal constant _ROLE_2 = 1 << 2;
            uint256 internal constant _ROLE_3 = 1 << 3;
            uint256 internal constant _ROLE_4 = 1 << 4;
            uint256 internal constant _ROLE_5 = 1 << 5;
            uint256 internal constant _ROLE_6 = 1 << 6;
            uint256 internal constant _ROLE_7 = 1 << 7;
            uint256 internal constant _ROLE_8 = 1 << 8;
            uint256 internal constant _ROLE_9 = 1 << 9;
            uint256 internal constant _ROLE_10 = 1 << 10;
            uint256 internal constant _ROLE_11 = 1 << 11;
            uint256 internal constant _ROLE_12 = 1 << 12;
            uint256 internal constant _ROLE_13 = 1 << 13;
            uint256 internal constant _ROLE_14 = 1 << 14;
            uint256 internal constant _ROLE_15 = 1 << 15;
            uint256 internal constant _ROLE_16 = 1 << 16;
            uint256 internal constant _ROLE_17 = 1 << 17;
            uint256 internal constant _ROLE_18 = 1 << 18;
            uint256 internal constant _ROLE_19 = 1 << 19;
            uint256 internal constant _ROLE_20 = 1 << 20;
            uint256 internal constant _ROLE_21 = 1 << 21;
            uint256 internal constant _ROLE_22 = 1 << 22;
            uint256 internal constant _ROLE_23 = 1 << 23;
            uint256 internal constant _ROLE_24 = 1 << 24;
            uint256 internal constant _ROLE_25 = 1 << 25;
            uint256 internal constant _ROLE_26 = 1 << 26;
            uint256 internal constant _ROLE_27 = 1 << 27;
            uint256 internal constant _ROLE_28 = 1 << 28;
            uint256 internal constant _ROLE_29 = 1 << 29;
            uint256 internal constant _ROLE_30 = 1 << 30;
            uint256 internal constant _ROLE_31 = 1 << 31;
            uint256 internal constant _ROLE_32 = 1 << 32;
            uint256 internal constant _ROLE_33 = 1 << 33;
            uint256 internal constant _ROLE_34 = 1 << 34;
            uint256 internal constant _ROLE_35 = 1 << 35;
            uint256 internal constant _ROLE_36 = 1 << 36;
            uint256 internal constant _ROLE_37 = 1 << 37;
            uint256 internal constant _ROLE_38 = 1 << 38;
            uint256 internal constant _ROLE_39 = 1 << 39;
            uint256 internal constant _ROLE_40 = 1 << 40;
            uint256 internal constant _ROLE_41 = 1 << 41;
            uint256 internal constant _ROLE_42 = 1 << 42;
            uint256 internal constant _ROLE_43 = 1 << 43;
            uint256 internal constant _ROLE_44 = 1 << 44;
            uint256 internal constant _ROLE_45 = 1 << 45;
            uint256 internal constant _ROLE_46 = 1 << 46;
            uint256 internal constant _ROLE_47 = 1 << 47;
            uint256 internal constant _ROLE_48 = 1 << 48;
            uint256 internal constant _ROLE_49 = 1 << 49;
            uint256 internal constant _ROLE_50 = 1 << 50;
            uint256 internal constant _ROLE_51 = 1 << 51;
            uint256 internal constant _ROLE_52 = 1 << 52;
            uint256 internal constant _ROLE_53 = 1 << 53;
            uint256 internal constant _ROLE_54 = 1 << 54;
            uint256 internal constant _ROLE_55 = 1 << 55;
            uint256 internal constant _ROLE_56 = 1 << 56;
            uint256 internal constant _ROLE_57 = 1 << 57;
            uint256 internal constant _ROLE_58 = 1 << 58;
            uint256 internal constant _ROLE_59 = 1 << 59;
            uint256 internal constant _ROLE_60 = 1 << 60;
            uint256 internal constant _ROLE_61 = 1 << 61;
            uint256 internal constant _ROLE_62 = 1 << 62;
            uint256 internal constant _ROLE_63 = 1 << 63;
            uint256 internal constant _ROLE_64 = 1 << 64;
            uint256 internal constant _ROLE_65 = 1 << 65;
            uint256 internal constant _ROLE_66 = 1 << 66;
            uint256 internal constant _ROLE_67 = 1 << 67;
            uint256 internal constant _ROLE_68 = 1 << 68;
            uint256 internal constant _ROLE_69 = 1 << 69;
            uint256 internal constant _ROLE_70 = 1 << 70;
            uint256 internal constant _ROLE_71 = 1 << 71;
            uint256 internal constant _ROLE_72 = 1 << 72;
            uint256 internal constant _ROLE_73 = 1 << 73;
            uint256 internal constant _ROLE_74 = 1 << 74;
            uint256 internal constant _ROLE_75 = 1 << 75;
            uint256 internal constant _ROLE_76 = 1 << 76;
            uint256 internal constant _ROLE_77 = 1 << 77;
            uint256 internal constant _ROLE_78 = 1 << 78;
            uint256 internal constant _ROLE_79 = 1 << 79;
            uint256 internal constant _ROLE_80 = 1 << 80;
            uint256 internal constant _ROLE_81 = 1 << 81;
            uint256 internal constant _ROLE_82 = 1 << 82;
            uint256 internal constant _ROLE_83 = 1 << 83;
            uint256 internal constant _ROLE_84 = 1 << 84;
            uint256 internal constant _ROLE_85 = 1 << 85;
            uint256 internal constant _ROLE_86 = 1 << 86;
            uint256 internal constant _ROLE_87 = 1 << 87;
            uint256 internal constant _ROLE_88 = 1 << 88;
            uint256 internal constant _ROLE_89 = 1 << 89;
            uint256 internal constant _ROLE_90 = 1 << 90;
            uint256 internal constant _ROLE_91 = 1 << 91;
            uint256 internal constant _ROLE_92 = 1 << 92;
            uint256 internal constant _ROLE_93 = 1 << 93;
            uint256 internal constant _ROLE_94 = 1 << 94;
            uint256 internal constant _ROLE_95 = 1 << 95;
            uint256 internal constant _ROLE_96 = 1 << 96;
            uint256 internal constant _ROLE_97 = 1 << 97;
            uint256 internal constant _ROLE_98 = 1 << 98;
            uint256 internal constant _ROLE_99 = 1 << 99;
            uint256 internal constant _ROLE_100 = 1 << 100;
            uint256 internal constant _ROLE_101 = 1 << 101;
            uint256 internal constant _ROLE_102 = 1 << 102;
            uint256 internal constant _ROLE_103 = 1 << 103;
            uint256 internal constant _ROLE_104 = 1 << 104;
            uint256 internal constant _ROLE_105 = 1 << 105;
            uint256 internal constant _ROLE_106 = 1 << 106;
            uint256 internal constant _ROLE_107 = 1 << 107;
            uint256 internal constant _ROLE_108 = 1 << 108;
            uint256 internal constant _ROLE_109 = 1 << 109;
            uint256 internal constant _ROLE_110 = 1 << 110;
            uint256 internal constant _ROLE_111 = 1 << 111;
            uint256 internal constant _ROLE_112 = 1 << 112;
            uint256 internal constant _ROLE_113 = 1 << 113;
            uint256 internal constant _ROLE_114 = 1 << 114;
            uint256 internal constant _ROLE_115 = 1 << 115;
            uint256 internal constant _ROLE_116 = 1 << 116;
            uint256 internal constant _ROLE_117 = 1 << 117;
            uint256 internal constant _ROLE_118 = 1 << 118;
            uint256 internal constant _ROLE_119 = 1 << 119;
            uint256 internal constant _ROLE_120 = 1 << 120;
            uint256 internal constant _ROLE_121 = 1 << 121;
            uint256 internal constant _ROLE_122 = 1 << 122;
            uint256 internal constant _ROLE_123 = 1 << 123;
            uint256 internal constant _ROLE_124 = 1 << 124;
            uint256 internal constant _ROLE_125 = 1 << 125;
            uint256 internal constant _ROLE_126 = 1 << 126;
            uint256 internal constant _ROLE_127 = 1 << 127;
            uint256 internal constant _ROLE_128 = 1 << 128;
            uint256 internal constant _ROLE_129 = 1 << 129;
            uint256 internal constant _ROLE_130 = 1 << 130;
            uint256 internal constant _ROLE_131 = 1 << 131;
            uint256 internal constant _ROLE_132 = 1 << 132;
            uint256 internal constant _ROLE_133 = 1 << 133;
            uint256 internal constant _ROLE_134 = 1 << 134;
            uint256 internal constant _ROLE_135 = 1 << 135;
            uint256 internal constant _ROLE_136 = 1 << 136;
            uint256 internal constant _ROLE_137 = 1 << 137;
            uint256 internal constant _ROLE_138 = 1 << 138;
            uint256 internal constant _ROLE_139 = 1 << 139;
            uint256 internal constant _ROLE_140 = 1 << 140;
            uint256 internal constant _ROLE_141 = 1 << 141;
            uint256 internal constant _ROLE_142 = 1 << 142;
            uint256 internal constant _ROLE_143 = 1 << 143;
            uint256 internal constant _ROLE_144 = 1 << 144;
            uint256 internal constant _ROLE_145 = 1 << 145;
            uint256 internal constant _ROLE_146 = 1 << 146;
            uint256 internal constant _ROLE_147 = 1 << 147;
            uint256 internal constant _ROLE_148 = 1 << 148;
            uint256 internal constant _ROLE_149 = 1 << 149;
            uint256 internal constant _ROLE_150 = 1 << 150;
            uint256 internal constant _ROLE_151 = 1 << 151;
            uint256 internal constant _ROLE_152 = 1 << 152;
            uint256 internal constant _ROLE_153 = 1 << 153;
            uint256 internal constant _ROLE_154 = 1 << 154;
            uint256 internal constant _ROLE_155 = 1 << 155;
            uint256 internal constant _ROLE_156 = 1 << 156;
            uint256 internal constant _ROLE_157 = 1 << 157;
            uint256 internal constant _ROLE_158 = 1 << 158;
            uint256 internal constant _ROLE_159 = 1 << 159;
            uint256 internal constant _ROLE_160 = 1 << 160;
            uint256 internal constant _ROLE_161 = 1 << 161;
            uint256 internal constant _ROLE_162 = 1 << 162;
            uint256 internal constant _ROLE_163 = 1 << 163;
            uint256 internal constant _ROLE_164 = 1 << 164;
            uint256 internal constant _ROLE_165 = 1 << 165;
            uint256 internal constant _ROLE_166 = 1 << 166;
            uint256 internal constant _ROLE_167 = 1 << 167;
            uint256 internal constant _ROLE_168 = 1 << 168;
            uint256 internal constant _ROLE_169 = 1 << 169;
            uint256 internal constant _ROLE_170 = 1 << 170;
            uint256 internal constant _ROLE_171 = 1 << 171;
            uint256 internal constant _ROLE_172 = 1 << 172;
            uint256 internal constant _ROLE_173 = 1 << 173;
            uint256 internal constant _ROLE_174 = 1 << 174;
            uint256 internal constant _ROLE_175 = 1 << 175;
            uint256 internal constant _ROLE_176 = 1 << 176;
            uint256 internal constant _ROLE_177 = 1 << 177;
            uint256 internal constant _ROLE_178 = 1 << 178;
            uint256 internal constant _ROLE_179 = 1 << 179;
            uint256 internal constant _ROLE_180 = 1 << 180;
            uint256 internal constant _ROLE_181 = 1 << 181;
            uint256 internal constant _ROLE_182 = 1 << 182;
            uint256 internal constant _ROLE_183 = 1 << 183;
            uint256 internal constant _ROLE_184 = 1 << 184;
            uint256 internal constant _ROLE_185 = 1 << 185;
            uint256 internal constant _ROLE_186 = 1 << 186;
            uint256 internal constant _ROLE_187 = 1 << 187;
            uint256 internal constant _ROLE_188 = 1 << 188;
            uint256 internal constant _ROLE_189 = 1 << 189;
            uint256 internal constant _ROLE_190 = 1 << 190;
            uint256 internal constant _ROLE_191 = 1 << 191;
            uint256 internal constant _ROLE_192 = 1 << 192;
            uint256 internal constant _ROLE_193 = 1 << 193;
            uint256 internal constant _ROLE_194 = 1 << 194;
            uint256 internal constant _ROLE_195 = 1 << 195;
            uint256 internal constant _ROLE_196 = 1 << 196;
            uint256 internal constant _ROLE_197 = 1 << 197;
            uint256 internal constant _ROLE_198 = 1 << 198;
            uint256 internal constant _ROLE_199 = 1 << 199;
            uint256 internal constant _ROLE_200 = 1 << 200;
            uint256 internal constant _ROLE_201 = 1 << 201;
            uint256 internal constant _ROLE_202 = 1 << 202;
            uint256 internal constant _ROLE_203 = 1 << 203;
            uint256 internal constant _ROLE_204 = 1 << 204;
            uint256 internal constant _ROLE_205 = 1 << 205;
            uint256 internal constant _ROLE_206 = 1 << 206;
            uint256 internal constant _ROLE_207 = 1 << 207;
            uint256 internal constant _ROLE_208 = 1 << 208;
            uint256 internal constant _ROLE_209 = 1 << 209;
            uint256 internal constant _ROLE_210 = 1 << 210;
            uint256 internal constant _ROLE_211 = 1 << 211;
            uint256 internal constant _ROLE_212 = 1 << 212;
            uint256 internal constant _ROLE_213 = 1 << 213;
            uint256 internal constant _ROLE_214 = 1 << 214;
            uint256 internal constant _ROLE_215 = 1 << 215;
            uint256 internal constant _ROLE_216 = 1 << 216;
            uint256 internal constant _ROLE_217 = 1 << 217;
            uint256 internal constant _ROLE_218 = 1 << 218;
            uint256 internal constant _ROLE_219 = 1 << 219;
            uint256 internal constant _ROLE_220 = 1 << 220;
            uint256 internal constant _ROLE_221 = 1 << 221;
            uint256 internal constant _ROLE_222 = 1 << 222;
            uint256 internal constant _ROLE_223 = 1 << 223;
            uint256 internal constant _ROLE_224 = 1 << 224;
            uint256 internal constant _ROLE_225 = 1 << 225;
            uint256 internal constant _ROLE_226 = 1 << 226;
            uint256 internal constant _ROLE_227 = 1 << 227;
            uint256 internal constant _ROLE_228 = 1 << 228;
            uint256 internal constant _ROLE_229 = 1 << 229;
            uint256 internal constant _ROLE_230 = 1 << 230;
            uint256 internal constant _ROLE_231 = 1 << 231;
            uint256 internal constant _ROLE_232 = 1 << 232;
            uint256 internal constant _ROLE_233 = 1 << 233;
            uint256 internal constant _ROLE_234 = 1 << 234;
            uint256 internal constant _ROLE_235 = 1 << 235;
            uint256 internal constant _ROLE_236 = 1 << 236;
            uint256 internal constant _ROLE_237 = 1 << 237;
            uint256 internal constant _ROLE_238 = 1 << 238;
            uint256 internal constant _ROLE_239 = 1 << 239;
            uint256 internal constant _ROLE_240 = 1 << 240;
            uint256 internal constant _ROLE_241 = 1 << 241;
            uint256 internal constant _ROLE_242 = 1 << 242;
            uint256 internal constant _ROLE_243 = 1 << 243;
            uint256 internal constant _ROLE_244 = 1 << 244;
            uint256 internal constant _ROLE_245 = 1 << 245;
            uint256 internal constant _ROLE_246 = 1 << 246;
            uint256 internal constant _ROLE_247 = 1 << 247;
            uint256 internal constant _ROLE_248 = 1 << 248;
            uint256 internal constant _ROLE_249 = 1 << 249;
            uint256 internal constant _ROLE_250 = 1 << 250;
            uint256 internal constant _ROLE_251 = 1 << 251;
            uint256 internal constant _ROLE_252 = 1 << 252;
            uint256 internal constant _ROLE_253 = 1 << 253;
            uint256 internal constant _ROLE_254 = 1 << 254;
            uint256 internal constant _ROLE_255 = 1 << 255;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeApproveCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeApproveCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeApproveERC721 hook that is called by a core token before approving a token.
             *
             *  @param _from The address that is approving tokens.
             *  @param _to The address that is being approved.
             *  @param _tokenId The token ID being approved.
             *  @param _approve The approval status to set.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeApproveERC721(address _from, address _to, uint256 _tokenId, bool _approve)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeApproveCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeApproveForAllCallback {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeApproveForAllCallbackNotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeApproveForAll hook that is called by a core token before approving an operator to transfer all tokens.
             *
             *  @param _from The address that is approving tokens.
             *  @param _to The address that is being approved.
             *  @param _approved Whether to grant or revoke approval.
             */
            function beforeApproveForAll(address _from, address _to, bool _approved)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeApproveForAllCallbackNotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeBurnCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeBurnCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeBurnERC721 hook that is called by a core token before burning a token.
             *
             *  @param _tokenId The token ID being burned.
             *  @param _data The encoded arguments for the beforeBurn hook.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeBurnERC721(uint256 _tokenId, bytes memory _data)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert BeforeBurnCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeMintCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeMintCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _startTokenId The token ID being minted.
             *  @param _amount The amount of tokens to mint.
             *  @param _data Optional extra data passed to the hook.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeMintERC721(address _to, uint256 _startTokenId, uint256 _amount, bytes memory _data)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert BeforeMintCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeMintWithSignatureCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeMintWithSignatureCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintWithSignatureERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _startTokenId The token ID being minted.
             *  @param _amount The amount of tokens to mint.
             *  @param _data Optional extra data passed to the hook.
             *  @param _signer The address that signed the minting request.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeMintWithSignatureERC721(
                address _to,
                uint256 _startTokenId,
                uint256 _amount,
                bytes memory _data,
                address _signer
            ) external payable virtual returns (bytes memory result) {
                revert BeforeMintWithSignatureCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract BeforeTransferCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error BeforeTransferCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeTransferERC721 hook that is called by a core token before transferring a token.
             *
             *  @param _from The address that is transferring tokens.
             *  @param _to The address that is receiving tokens.
             *  @param _tokenId The token ID being transferred.
             *  @return result Abi encoded bytes result of the hook.
             */
            function beforeTransferERC721(address _from, address _to, uint256 _tokenId)
                external
                virtual
                returns (bytes memory result)
            {
                revert BeforeTransferCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract OnTokenURICallback {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error OnTokenURICallbackNotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Returns the URI to fetch token metadata from.
             *  @dev Meant to be called by the core token contract.
             *  @param _tokenId The token ID of the NFT.
             *  @return metadata The URI to fetch token metadata from.
             */
            function onTokenURI(uint256 _tokenId) external view virtual returns (string memory metadata) {
                revert OnTokenURICallbackNotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        contract UpdateMetadataCallbackERC721 {
            /*//////////////////////////////////////////////////////////////
                                        ERRORS
            //////////////////////////////////////////////////////////////*/
            error UpdateMetadataCallbackERC721NotImplemented();
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice The beforeMintERC721 hook that is called by a core token before minting tokens.
             *
             *  @param _to The address that is minting tokens.
             *  @param _amount The amount of tokens to mint.
             *  @param _baseURI The URI to fetch token metadata from.
             *  @return result Abi encoded bytes result of the hook.
             */
            function updateMetadataERC721(address _to, uint256 _startTokenId, uint256 _amount, string calldata _baseURI)
                external
                payable
                virtual
                returns (bytes memory result)
            {
                revert UpdateMetadataCallbackERC721NotImplemented();
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {
            ERC721AQueryableUpgradeable,
            ERC721AUpgradeable,
            IERC721AUpgradeable
        } from "@erc721a-upgradeable/extensions/ERC721AQueryableUpgradeable.sol";
        import {ECDSA} from "@solady/utils/ECDSA.sol";
        import {EIP712} from "@solady/utils/EIP712.sol";
        import {Multicallable} from "@solady/utils/Multicallable.sol";
        import {Core} from "../../Core.sol";
        import {BeforeApproveCallbackERC721} from "../../callback/BeforeApproveCallbackERC721.sol";
        import {BeforeApproveForAllCallback} from "../../callback/BeforeApproveForAllCallback.sol";
        import {BeforeBurnCallbackERC721} from "../../callback/BeforeBurnCallbackERC721.sol";
        import {BeforeMintCallbackERC721} from "../../callback/BeforeMintCallbackERC721.sol";
        import {BeforeMintWithSignatureCallbackERC721} from "../../callback/BeforeMintWithSignatureCallbackERC721.sol";
        import {BeforeTransferCallbackERC721} from "../../callback/BeforeTransferCallbackERC721.sol";
        import {UpdateMetadataCallbackERC721} from "../../callback/UpdateMetadataCallbackERC721.sol";
        import {OnTokenURICallback} from "../../callback/OnTokenURICallback.sol";
        contract ERC721Base is ERC721AQueryableUpgradeable, Core, Multicallable, EIP712 {
            using ECDSA for bytes32;
            /*//////////////////////////////////////////////////////////////
                                        CONSTANTS
            //////////////////////////////////////////////////////////////*/
            bytes32 private constant TYPEHASH_SIGNATURE_MINT_ERC721 =
                keccak256("MintRequestERC721(address to,uint256 amount,string baseURI,bytes data)");
            /*//////////////////////////////////////////////////////////////
                                        STORAGE
            //////////////////////////////////////////////////////////////*/
            /// @notice The contract metadata URI of the contract.
            string private contractURI_;
            /*//////////////////////////////////////////////////////////////
                                       EVENTS
            //////////////////////////////////////////////////////////////*/
            /// @notice Emitted when the contract URI is updated.
            event ContractURIUpdated();
            /*//////////////////////////////////////////////////////////////
                                    CONSTRUCTOR
            //////////////////////////////////////////////////////////////*/
            function _initialize(
                string memory _name,
                string memory _symbol,
                string memory _contractURI,
                address _owner,
                address[] memory _modules,
                bytes[] memory _moduleInstallData
            ) internal initializerERC721A {
                // Set contract metadata
                __ERC721A_init(_name, _symbol);
                _setupContractURI(_contractURI);
                _initializeOwner(_owner);
                // Install and initialize modules
                require(_modules.length == _moduleInstallData.length);
                for (uint256 i = 0; i < _modules.length; i++) {
                    _installModule(_modules[i], _moduleInstallData[i]);
                }
            }
            /*//////////////////////////////////////////////////////////////
                                      VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Returns the contract URI of the contract.
             *  @return uri The contract URI of the contract.
             */
            function contractURI() external view returns (string memory) {
                return contractURI_;
            }
            /// @notice Returns the starting token ID for sequential mints.
            function startTokenId() external view returns (uint256) {
                return _startTokenId();
            }
            /// @notice Returns the total number of tokens minted ever.
            function totalMinted() external view returns (uint256) {
                return _totalMinted();
            }
            /**
             *  @notice Returns the token metadata of an NFT.
             *  @dev Always returns metadata queried from the metadata source.
             *  @param id The token ID of the NFT.
             *  @return metadata The URI to fetch metadata from.
             */
            function tokenURI(uint256 id)
                public
                view
                override(ERC721AUpgradeable, IERC721AUpgradeable)
                returns (string memory)
            {
                return _getTokenURI(id);
            }
            /**
             *  @notice Returns whether the contract implements an interface with the given interface ID.
             *  @param interfaceId The interface ID of the interface to check for
             */
            function supportsInterface(bytes4 interfaceId)
                public
                view
                override(ERC721AUpgradeable, IERC721AUpgradeable, Core)
                returns (bool)
            {
                return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
                    || interfaceId == 0x80ac58cd // ERC165 Interface ID for ERC721
                    || interfaceId == 0x5b5e139f // ERC165 Interface ID for ERC721Metadata
                    || interfaceId == 0xe8a3d485 // ERC-7572
                    || interfaceId == 0x7f5828d0 // ERC-173
                    || super.supportsInterface(interfaceId); // right-most Core
            }
            function getSupportedCallbackFunctions()
                public
                pure
                override
                returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
            {
                supportedCallbackFunctions = new SupportedCallbackFunction[](8);
                supportedCallbackFunctions[0] = SupportedCallbackFunction({
                    selector: BeforeMintCallbackERC721.beforeMintERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
                supportedCallbackFunctions[1] = SupportedCallbackFunction({
                    selector: BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
                supportedCallbackFunctions[2] = SupportedCallbackFunction({
                    selector: BeforeTransferCallbackERC721.beforeTransferERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[3] = SupportedCallbackFunction({
                    selector: BeforeBurnCallbackERC721.beforeBurnERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[4] = SupportedCallbackFunction({
                    selector: BeforeApproveCallbackERC721.beforeApproveERC721.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[5] = SupportedCallbackFunction({
                    selector: BeforeApproveForAllCallback.beforeApproveForAll.selector,
                    mode: CallbackMode.OPTIONAL
                });
                supportedCallbackFunctions[6] =
                    SupportedCallbackFunction({selector: OnTokenURICallback.onTokenURI.selector, mode: CallbackMode.REQUIRED});
                supportedCallbackFunctions[7] = SupportedCallbackFunction({
                    selector: UpdateMetadataCallbackERC721.updateMetadataERC721.selector,
                    mode: CallbackMode.REQUIRED
                });
            }
            /*//////////////////////////////////////////////////////////////
                                EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @notice Sets the contract URI of the contract.
             *  @dev Only callable by contract admin.
             *  @param uri The contract URI to set.
             */
            function setContractURI(string memory uri) external onlyOwner {
                _setupContractURI(uri);
            }
            /**
             *  @notice Mints a token. Calls the beforeMint hook.
             *  @dev Reverts if beforeMint hook is absent or unsuccessful.
             *  @param to The address to mint the token to.
             *  @param amount The amount of tokens to mint.
             *  @param data ABI encoded data to pass to the beforeMint hook.
             */
            function mint(address to, uint256 amount, string calldata baseURI, bytes calldata data) external payable {
                uint256 tokenId = _nextTokenId();
                if (bytes(baseURI).length > 0) {
                    _updateMetadata(to, tokenId, amount, baseURI);
                }
                _beforeMint(to, tokenId, amount, data);
                _safeMint(to, amount, "");
            }
            /**
             *  @notice Mints a token with a signature. Calls the beforeMint hook.
             *  @dev Reverts if beforeMint hook is absent or unsuccessful.
             *  @param to The address to mint the token to.
             *  @param amount The amount of tokens to mint.
             *  @param data ABI encoded data to pass to the beforeMint hook.
             *  @param signature The signature produced from signing the minting request.
             */
            function mintWithSignature(
                address to,
                uint256 amount,
                string calldata baseURI,
                bytes calldata data,
                bytes memory signature
            ) external payable {
                address signer = _hashTypedData(
                    keccak256(
                        abi.encode(TYPEHASH_SIGNATURE_MINT_ERC721, to, amount, keccak256(bytes(baseURI)), keccak256(data))
                    )
                ).recover(signature);
                uint256 tokenId = _nextTokenId();
                if (bytes(baseURI).length > 0) {
                    _updateMetadata(to, tokenId, amount, baseURI);
                }
                _beforeMintWithSignature(to, tokenId, amount, data, signer);
                _safeMint(to, amount, "");
            }
            /**
             *  @notice Burns an NFT.
             *  @dev Calls the beforeBurn hook. Skips calling the hook if it doesn't exist.
             *  @param tokenId The token ID of the NFT to burn.
             *  @param data ABI encoded data to pass to the beforeBurn hook.
             */
            function burn(uint256 tokenId, bytes calldata data) external payable {
                _beforeBurn(tokenId, data);
                _burn(tokenId, true);
            }
            /**
             *  @notice Transfers ownership of an NFT from one address to another.
             *  @dev Overriden to call the beforeTransfer hook. Skips calling the hook if it doesn't exist.
             *  @param from The address to transfer from
             *  @param to The address to transfer to
             *  @param id The token ID of the NFT
             */
            function transferFrom(address from, address to, uint256 id)
                public
                payable
                override(ERC721AUpgradeable, IERC721AUpgradeable)
            {
                _beforeTransfer(from, to, id);
                super.transferFrom(from, to, id);
            }
            /**
             *  @notice Approves an address to transfer a specific NFT. Reverts if caller is not owner or approved operator.
             *  @dev Overriden to call the beforeApprove hook. Skips calling the hook if it doesn't exist.
             *  @param spender The address to approve
             *  @param id The token ID of the NFT
             */
            function approve(address spender, uint256 id) public payable override(ERC721AUpgradeable, IERC721AUpgradeable) {
                _beforeApprove(msg.sender, spender, id, true);
                super.approve(spender, id);
            }
            /**
             *  @notice Approves or revokes approval from an operator to transfer or issue approval for all of the caller's NFTs.
             *  @param operator The address to approve or revoke approval from
             *  @param approved Whether the operator is approved
             */
            function setApprovalForAll(address operator, bool approved)
                public
                override(ERC721AUpgradeable, IERC721AUpgradeable)
            {
                _beforeApproveForAll(msg.sender, operator, approved);
                super.setApprovalForAll(operator, approved);
            }
            /*//////////////////////////////////////////////////////////////
                                    INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Sets contract URI
            function _setupContractURI(string memory _contractURI) internal {
                contractURI_ = _contractURI;
                emit ContractURIUpdated();
            }
            /*//////////////////////////////////////////////////////////////
                                CALLBACK INTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Calls the beforeMint hook.
            function _beforeMint(address to, uint256 startTokenId, uint256 amount, bytes calldata data) internal virtual {
                _executeCallbackFunction(
                    BeforeMintCallbackERC721.beforeMintERC721.selector,
                    abi.encodeCall(BeforeMintCallbackERC721.beforeMintERC721, (to, startTokenId, amount, data))
                );
            }
            /// @dev Calls the beforeMint hook.
            function _beforeMintWithSignature(
                address to,
                uint256 startTokenId,
                uint256 amount,
                bytes calldata data,
                address signer
            ) internal virtual {
                _executeCallbackFunction(
                    BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721.selector,
                    abi.encodeCall(
                        BeforeMintWithSignatureCallbackERC721.beforeMintWithSignatureERC721,
                        (to, startTokenId, amount, data, signer)
                    )
                );
            }
            /// @dev Calls the beforeTransfer hook, if installed.
            function _beforeTransfer(address from, address to, uint256 tokenId) internal virtual {
                _executeCallbackFunction(
                    BeforeTransferCallbackERC721.beforeTransferERC721.selector,
                    abi.encodeCall(BeforeTransferCallbackERC721.beforeTransferERC721, (from, to, tokenId))
                );
            }
            /// @dev Calls the beforeBurn hook, if installed.
            function _beforeBurn(uint256 tokenId, bytes calldata data) internal virtual {
                _executeCallbackFunction(
                    BeforeBurnCallbackERC721.beforeBurnERC721.selector,
                    abi.encodeCall(BeforeBurnCallbackERC721.beforeBurnERC721, (tokenId, data))
                );
            }
            /// @dev Calls the beforeApprove hook, if installed.
            function _beforeApprove(address from, address to, uint256 tokenId, bool approved) internal virtual {
                _executeCallbackFunction(
                    BeforeApproveCallbackERC721.beforeApproveERC721.selector,
                    abi.encodeCall(BeforeApproveCallbackERC721.beforeApproveERC721, (from, to, tokenId, approved))
                );
            }
            /// @dev Calls the beforeApprove hook, if installed.
            function _beforeApproveForAll(address from, address to, bool approved) internal virtual {
                _executeCallbackFunction(
                    BeforeApproveForAllCallback.beforeApproveForAll.selector,
                    abi.encodeCall(BeforeApproveForAllCallback.beforeApproveForAll, (from, to, approved))
                );
            }
            /// @dev Calls the updateMetadata hook, if installed.
            function _updateMetadata(address to, uint256 startTokenId, uint256 amount, string calldata baseURI)
                internal
                virtual
            {
                _executeCallbackFunction(
                    UpdateMetadataCallbackERC721.updateMetadataERC721.selector,
                    abi.encodeCall(UpdateMetadataCallbackERC721.updateMetadataERC721, (to, startTokenId, amount, baseURI))
                );
            }
            /// @dev Fetches token URI from the token metadata hook.
            function _getTokenURI(uint256 tokenId) internal view virtual returns (string memory uri) {
                (, bytes memory returndata) = _executeCallbackFunctionView(
                    OnTokenURICallback.onTokenURI.selector, abi.encodeCall(OnTokenURICallback.onTokenURI, (tokenId))
                );
                uri = abi.decode(returndata, (string));
            }
            /// @dev Returns the domain name and version for EIP712.
            function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
                name = "ERC721Core";
                version = "1";
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {ERC721Base} from "./ERC721Base.sol";
        import {Initializable} from "@solady/utils/Initializable.sol";
        contract ERC721CoreInitializable is ERC721Base, Initializable {
            constructor() {
                _disableInitializers();
            }
            function initialize(
                string memory _name,
                string memory _symbol,
                string memory _contractURI,
                address _owner,
                address[] memory _modules,
                bytes[] memory _moduleInstallData
            ) external payable initializer {
                _initialize(_name, _symbol, _contractURI, _owner, _modules, _moduleInstallData);
            }
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {IERC165} from "./IERC165.sol";
        import {IModuleConfig} from "./IModuleConfig.sol";
        interface ICore is IModuleConfig, IERC165 {
            /*//////////////////////////////////////////////////////////////
                                    STRUCTS & ENUMS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Whether execution reverts when the callback function is not implemented by any installed Module.
             *  @param OPTIONAL Execution does not revert when the callback function is not implemented.
             *  @param REQUIRED Execution reverts when the callback function is not implemented.
             */
            enum CallbackMode {
                OPTIONAL,
                REQUIRED
            }
            /**
             *  @dev Struct representing a callback function called on an Module during some fixed function's execution.
             *  @param selector The 4-byte function selector of the callback function.
             *  @param mode Whether execution reverts when the callback function is not implemented by any installed Module.
             */
            struct SupportedCallbackFunction {
                bytes4 selector;
                CallbackMode mode;
            }
            /**
             *  @dev Struct representing an installed Module.
             *  @param implementation The address of the Module contract.
             *  @param config The Module Config of the Module contract.
             */
            struct InstalledModule {
                address implementation;
                ModuleConfig config;
            }
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /// @dev Returns all callback function calls made to Modules at some point during a fixed function's execution.
            function getSupportedCallbackFunctions() external pure returns (SupportedCallbackFunction[] memory);
            /// @dev Returns all installed modules and their respective module configs.
            function getInstalledModules() external view returns (InstalledModule[] memory);
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Installs an Module in the Core.
             *
             *  @param moduleContract The address of the Module contract to be installed.
             *  @param data The data to be passed to the Module's onInstall callback function.
             *
             *  MUST implement authorization control.
             *  MUST call `onInstall` callback function if Module Config has registerd for installation callbacks.
             *  MUST revert if Core does not implement the interface required by the Module, specified in the Module Config.
             *  MUST revert if any callback or fallback function in the Module's ModuleConfig is already registered in the Core with another Module.
             *
             *  MAY interpret the provided address as the implementation address of the Module contract to install as a proxy.
             */
            function installModule(address moduleContract, bytes calldata data) external payable;
            /**
             *  @dev Uninstalls an Module from the Core.
             *
             *  @param moduleContract The address of the Module contract to be uninstalled.
             *  @param data The data to be passed to the Module's onUninstall callback function.
             *
             *  MUST implement authorization control.
             *  MUST call `onUninstall` callback function if Module Config has registerd for installation callbacks.
             *
             *  MAY interpret the provided address as the implementation address of the Module contract which is installed as a proxy.
             */
            function uninstallModule(address moduleContract, bytes calldata data) external payable;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IERC165 {
            /// @notice Query if a contract implements an interface
            /// @param interfaceID The interface identifier, as specified in ERC-165
            /// @dev Interface identification is specified in ERC-165. This function
            ///  uses less than 30,000 gas.
            /// @return `true` if the contract implements `interfaceID` and
            ///  `interfaceID` is not 0xffffffff, `false` otherwise
            function supportsInterface(bytes4 interfaceID) external view returns (bool);
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IInstallationCallback {
            /*//////////////////////////////////////////////////////////////
                                    EXTERNAL FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Called by a Core into an Module during the installation of the Module.
             *
             *  @param data The data passed to the Core's installModule function.
             */
            function onInstall(bytes calldata data) external;
            /**
             *  @dev Called by a Core into an Module during the uninstallation of the Module.
             *
             *  @param data The data passed to the Core's uninstallModule function.
             */
            function onUninstall(bytes calldata data) external;
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        import {IModuleConfig} from "./IModuleConfig.sol";
        interface IModule is IModuleConfig {
            /*//////////////////////////////////////////////////////////////
                                    VIEW FUNCTIONS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Returns the ModuleConfig of the Module contract.
             */
            function getModuleConfig() external pure returns (ModuleConfig memory);
        }
        // SPDX-License-Identifier: Apache-2.0
        pragma solidity ^0.8.20;
        interface IModuleConfig {
            /*//////////////////////////////////////////////////////////////
                                    STRUCTS & ENUMS
            //////////////////////////////////////////////////////////////*/
            /**
             *  @dev Struct for a callback function. Called by a Core into an Module during the execution of some fixed function.
             *
             *  @param selector The 4-byte selector of the function.
             *  @param callType The type of call to be made to the function.
             */
            struct CallbackFunction {
                bytes4 selector;
            }
            /**
             *  @dev Struct for a fallback function. Called by a Core into an Module via the Core's fallback.
             *
             *  @param selector The 4-byte selector of the function.
             *  @param callType The type of call to be made to the function.
             *  @param permissionBits Core’s fallback function MUST check that msg.sender has these permissions before
             *                        performing a call on the Module. (OPTIONAL field)
             */
            struct FallbackFunction {
                bytes4 selector;
                uint256 permissionBits;
            }
            /**
             *  @dev Struct containing all information that a Core uses to check whether an Module is compatible for installation.
             *
             *  @param registerInstallationCallback Whether the Module expects onInstall and onUninstall callback function calls at
             *                                      installation and uninstallation time, respectively
             *  @param requiredInterfaces The ERC-165 interface that a Core MUST support to be compatible for installation. OPTIONAL -- can be bytes4(0)
             *                             if there is no required interface id.
             *  @param supportedInterfaces The ERC-165 interfaces that a Core supports upon installing the Module.
             *  @param callbackFunctions List of callback functions that the Core MUST call at some point in the execution of its fixed functions.
             *  @param fallbackFunctions List of functions that the Core MUST call via its fallback function with the Module as the call destination.
             */
            struct ModuleConfig {
                bool registerInstallationCallback;
                bytes4[] requiredInterfaces;
                bytes4[] supportedInterfaces;
                CallbackFunction[] callbackFunctions;
                FallbackFunction[] fallbackFunctions;
            }
        }