ETH Price: $1,844.06 (+1.66%)
    /

    Contract Diff Checker

    Contract Name:
    FinalBosuBadges

    Contract Source Code:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.28;
    
    import {ERC1155} from "solady/src/tokens/ext/zksync/ERC1155.sol";
    import {Ownable} from "solady/src/auth/Ownable.sol";
    import {Base64} from "solady/src/utils/Base64.sol";
    import {LibString} from "solady/src/utils/LibString.sol";
    import {ECDSA} from "solady/src/utils/ECDSA.sol";
    import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol"; 
    import {IERC1155MetadataURI} from "@openzeppelin/contracts/interfaces/IERC1155MetadataURI.sol";
    
    /**
     * @title FinalBosuBadges
     * @author @finalbosuX
     * @notice Soulbound ERC1155 contract
     */
    contract FinalBosuBadges is ERC1155, Ownable {
        using LibString for uint256;
    
        /**
         * @dev Thrown on user-to-user transfer if the token is soulbound.
         */
        error Soulbound();
    
        /**
         * @dev Thrown if `msg.sender` is neither owner nor manager.
         */
        error NotManager();
    
        /**
         * @dev Thrown if an input address is zero.
         */
        error ZeroAddress();
    
        /**
         * @dev Thrown if token metadata is not created before use.
         */
        error TokenNotCreated();
    
        /**
         * @dev Thrown if signature validation fails.
         */
        error InvalidSignature();
    
        /**
         * @dev Thrown if a given nonce has already been used.
         */
        error NonceUsed();
    
        /**
         * @notice Tracks which addresses can mint or burn tokens (besides the owner).
         */
        mapping(address => bool) public managers;
    
        /**
         * @dev Modifier that checks `msg.sender` is the owner or a manager.
         */
        modifier onlyOwnerOrManager() {
            if (msg.sender != owner() && !managers[msg.sender]) {
                revert NotManager();
            }
            _;
        }
    
        /**
         * @notice Sets or unsets `_manager` as a manager.
         * @param _manager Address to configure.
         * @param _status Boolean to grant or revoke.
         */
        function setManager(address _manager, bool _status) external onlyOwner {
            if (_manager == address(0)) revert ZeroAddress();
            managers[_manager] = _status;
        }
    
        /**
         * @notice Data structure for metadata.
         */
        struct OnChainMetadata {
            string name;
            string description;
            string imageUri;
            bool soulbound;
            uint256 createdAt;
        }
    
        /**
         * @notice Mapping from tokenId => metadata.
         */
        mapping(uint256 => OnChainMetadata) private _tokenMetadata;
    
        /**
         * @notice Contract name.
         */
        string public name;
    
        /**
         * @notice Contract symbol.
         */
        string public symbol;
    
        /**
         * @notice Constructor sets owner to deployer and optional manager status.
         */ 
        constructor() {
            _initializeOwner(msg.sender);
            name = "FinalBosuBadges";
            symbol = "FBB";
            managers[msg.sender] = true;
        }
    
        /**
         * @inheritdoc ERC1155
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) public virtual override {
            if (_tokenMetadata[id].soulbound) {
                if (from != address(0) && to != address(0)) {
                    revert Soulbound();
                }
            }
            super.safeTransferFrom(from, to, id, amount, data);
        }
    
        /**
         * @inheritdoc ERC1155
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) public virtual override {
            for (uint256 i = 0; i < ids.length; i++) {
                if (_tokenMetadata[ids[i]].soulbound) {
                    if (from != address(0) && to != address(0)) {
                        revert Soulbound();
                    }
                }
            }
            super.safeBatchTransferFrom(from, to, ids, amounts, data);
        }
    
        /**
         * @notice Admin or manager can mint.
         * @param to Recipient address.
         * @param id Token ID.
         * @param amount Quantity to mint.
         * @param data Extra data.
         */
        function mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) external onlyOwnerOrManager {
            if (to == address(0)) revert ZeroAddress();
            _mint(to, id, amount, data);
        } 
    
        /**
         * @notice Admin or manager can burn from another address.
         * @param from Address to burn from.
         * @param id Token ID.
         * @param amount Quantity to burn.
         */
        function burnFrom(
            address from,
            uint256 id,
            uint256 amount
        ) external onlyOwnerOrManager {
            _burn(from, id, amount);
        }
    
        /**
         * @notice Admin or manager can batch mint.
         * @param to Recipient address.
         * @param ids Array of token IDs.
         * @param amounts Array of amounts.
         * @param data Extra data.
         */
        function mintBatch(
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) external onlyOwnerOrManager {
            if (to == address(0)) revert ZeroAddress();
            require(ids.length == amounts.length, "Mismatch");
            _batchMint(to, ids, amounts, data);
        }
    
        /**
         * @notice Admin or manager can batch burn from another address.
         * @param from Address to burn from.
         * @param ids Array of token IDs.
         * @param amounts Array of amounts.
         */
        function burnBatch(
            address from,
            uint256[] calldata ids,
            uint256[] calldata amounts
        ) external onlyOwnerOrManager {
            require(ids.length == amounts.length, "Mismatch");
            _batchBurn(from, ids, amounts);
        }
    
        /**
         * @notice Self-burn for a user's own balance.
         * @param tokenId Token ID.
         * @param amount Quantity to burn.
         */
        function burn(uint256 tokenId, uint256 amount) external {
            uint256 bal = balanceOf(msg.sender, tokenId);
            require(bal >= amount, "Not enough balance");
            _burn(msg.sender, tokenId, amount);
        }
     
        /**
         * @notice Set metadata for `tokenId`.
         * @param tokenId Token ID.
         * @param _name Name field.
         * @param _description Description field.
         * @param _imageUri image uri.
         * @param _soulbound Whether token is soulbound.
         */
        function setTokenMetadata(
            uint256 tokenId,
            string calldata _name,
            string calldata _description,
            string calldata _imageUri,
            bool _soulbound
        ) external onlyOwnerOrManager {
            _tokenMetadata[tokenId] = OnChainMetadata({
                name: _name,
                description: _description,
                imageUri: _imageUri,
                soulbound: _soulbound,
                createdAt: block.timestamp
            }); 
        }
    
        /**
         * @notice Returns Base64-encoded JSON metadata for `tokenId`.
         * @param tokenId Token ID.
         * @return A data: URI containing the JSON metadata.
         */
        function uri(
            uint256 tokenId
        ) public view virtual override returns (string memory) {
            OnChainMetadata memory meta = _tokenMetadata[tokenId];
    
            if (meta.createdAt == 0) revert TokenNotCreated();
    
            string memory name_ = bytes(meta.name).length > 0
                ? meta.name
                : string(abi.encodePacked("Token #", tokenId.toString()));
    
            string memory desc_ = bytes(meta.description).length > 0
                ? meta.description
                : "No description.";
            
            string memory image_ = bytes(meta.imageUri).length > 0
                ? string(abi.encodePacked(meta.imageUri, tokenId.toString()))
                : "";
    
            string memory json = string(
                abi.encodePacked(
                    "{",
                    '"name":"',
                    LibString.escapeJSON(name_, false),
                    '","description":"',
                    LibString.escapeJSON(desc_, false),
                    '","image":"',
                    LibString.escapeJSON(image_, false),
                    '","soulbound":"',
                    meta.soulbound ? "YES" : "NO",
                    '"}'
                )
            );
    
            return
                string(
                    abi.encodePacked(
                        "data:application/json;base64,",
                        Base64.encode(bytes(json))
                    )
                );
        }
    
        /**
         * @notice ERC165 support.
         * @param interfaceId Interface ID.
         * @return True if supported.
         */
        function supportsInterface(
            bytes4 interfaceId
        ) public view virtual override returns (bool) {
            return
                super.supportsInterface(interfaceId) ||
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1155.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC1155} from "../token/ERC1155/IERC1155.sol";

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1155MetadataURI.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC1155MetadataURI} from "../token/ERC1155/extensions/IERC1155MetadataURI.sol";

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

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

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Library to encode strings in Base64.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
    /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <brecht@loopring.org>.
    library Base64 {
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// See: https://datatracker.ietf.org/doc/html/rfc4648
        /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
        /// @param noPadding Whether to strip away the padding.
        function encode(bytes memory data, bool fileSafe, bool noPadding)
            internal
            pure
            returns (string memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let dataLength := mload(data)
    
                if dataLength {
                    // Multiply by 4/3 rounded up.
                    // The `shl(2, ...)` is equivalent to multiplying by 4.
                    let encodedLength := shl(2, div(add(dataLength, 2), 3))
    
                    // Set `result` to point to the start of the free memory.
                    result := mload(0x40)
    
                    // Store the table into the scratch space.
                    // Offsetted by -1 byte so that the `mload` will load the character.
                    // We will rewrite the free memory pointer at `0x40` later with
                    // the allocated size.
                    // The magic constant 0x0670 will turn "-_" into "+/".
                    mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                    mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
    
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    let end := add(ptr, encodedLength)
    
                    let dataEnd := add(add(0x20, data), dataLength)
                    let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot.
                    mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits.
    
                    // Run over the input, 3 bytes at a time.
                    for {} 1 {} {
                        data := add(data, 3) // Advance 3 bytes.
                        let input := mload(data)
    
                        // Write 4 bytes. Optimized for fewer stack operations.
                        mstore8(0, mload(and(shr(18, input), 0x3F)))
                        mstore8(1, mload(and(shr(12, input), 0x3F)))
                        mstore8(2, mload(and(shr(6, input), 0x3F)))
                        mstore8(3, mload(and(input, 0x3F)))
                        mstore(ptr, mload(0x00))
    
                        ptr := add(ptr, 4) // Advance 4 bytes.
                        if iszero(lt(ptr, end)) { break }
                    }
                    mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`.
                    mstore(0x40, add(end, 0x20)) // Allocate the memory.
                    // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                    let o := div(2, mod(dataLength, 3))
                    // Offset `ptr` and pad with '='. We can simply write over the end.
                    mstore(sub(ptr, o), shl(240, 0x3d3d))
                    // Set `o` to zero if there is padding.
                    o := mul(iszero(iszero(noPadding)), o)
                    mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                    mstore(result, sub(encodedLength, o)) // Store the length.
                }
            }
        }
    
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, false, false)`.
        function encode(bytes memory data) internal pure returns (string memory result) {
            result = encode(data, false, false);
        }
    
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, fileSafe, false)`.
        function encode(bytes memory data, bool fileSafe)
            internal
            pure
            returns (string memory result)
        {
            result = encode(data, fileSafe, false);
        }
    
        /// @dev Decodes base64 encoded `data`.
        ///
        /// Supports:
        /// - RFC 4648 (both standard and file-safe mode).
        /// - RFC 3501 (63: ',').
        ///
        /// Does not support:
        /// - Line breaks.
        ///
        /// Note: For performance reasons,
        /// this function will NOT revert on invalid `data` inputs.
        /// Outputs for invalid inputs will simply be undefined behaviour.
        /// It is the user's responsibility to ensure that the `data`
        /// is a valid base64 encoded string.
        function decode(string memory data) internal pure returns (bytes memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                let dataLength := mload(data)
    
                if dataLength {
                    let decodedLength := mul(shr(2, dataLength), 3)
    
                    for {} 1 {} {
                        // If padded.
                        if iszero(and(dataLength, 3)) {
                            let t := xor(mload(add(data, dataLength)), 0x3d3d)
                            // forgefmt: disable-next-item
                            decodedLength := sub(
                                decodedLength,
                                add(iszero(byte(30, t)), iszero(byte(31, t)))
                            )
                            break
                        }
                        // If non-padded.
                        decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                        break
                    }
                    result := mload(0x40)
    
                    // Write the length of the bytes.
                    mstore(result, decodedLength)
    
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    let end := add(ptr, decodedLength)
    
                    // Load the table into the scratch space.
                    // Constants are optimized for smaller bytecode with zero gas overhead.
                    // `m` also doubles as the mask of the upper 6 bits.
                    let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                    mstore(0x5b, m)
                    mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                    mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
    
                    for {} 1 {} {
                        // Read 4 bytes.
                        data := add(data, 4)
                        let input := mload(data)
    
                        // Write 3 bytes.
                        // forgefmt: disable-next-item
                        mstore(ptr, or(
                            and(m, mload(byte(28, input))),
                            shr(6, or(
                                and(m, mload(byte(29, input))),
                                shr(6, or(
                                    and(m, mload(byte(30, input))),
                                    shr(6, mload(byte(31, input)))
                                ))
                            ))
                        ))
                        ptr := add(ptr, 3)
                        if iszero(lt(ptr, end)) { break }
                    }
                    mstore(0x40, add(end, 0x20)) // Allocate the memory.
                    mstore(end, 0) // Zeroize the slot after the bytes.
                    mstore(0x60, 0) // Restore the zero slot.
                }
            }
        }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Simple ERC1155 implementation.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ext/zksync/ERC1155.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol)
    ///
    /// @dev Note:
    /// - The ERC1155 standard allows for self-approvals.
    ///   For performance, this implementation WILL NOT revert for such actions.
    ///   Please add any checks with overrides if desired.
    ///
    /// If you are overriding:
    /// - Make sure all variables written to storage are properly cleaned
    //    (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
    /// - Check that the overridden function is actually used in the function you want to
    ///   change the behavior of. Much of the code has been manually inlined for performance.
    abstract contract ERC1155 {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The lengths of the input arrays are not the same.
        error ArrayLengthsMismatch();
    
        /// @dev Cannot mint or transfer to the zero address.
        error TransferToZeroAddress();
    
        /// @dev The recipient's balance has overflowed.
        error AccountBalanceOverflow();
    
        /// @dev Insufficient balance.
        error InsufficientBalance();
    
        /// @dev Only the token owner or an approved account can manage the tokens.
        error NotOwnerNorApproved();
    
        /// @dev Cannot safely transfer to a contract that does not implement
        /// the ERC1155Receiver interface.
        error TransferToNonERC1155ReceiverImplementer();
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Emitted when `amount` of token `id` is transferred
        /// from `from` to `to` by `operator`.
        event TransferSingle(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256 id,
            uint256 amount
        );
    
        /// @dev Emitted when `amounts` of token `ids` are transferred
        /// from `from` to `to` by `operator`.
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] amounts
        );
    
        /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
        event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
    
        /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
        /// is updated to `value`. This event is not used in the base contract.
        /// You may need to emit this event depending on your URI logic.
        ///
        /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
        event URI(string value, uint256 indexed id);
    
        /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
        uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
            0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
    
        /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
        uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
            0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
    
        /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
        uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
            0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The `ownerSlotSeed` of a given owner is given by.
        /// ```
        ///     let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))
        /// ```
        ///
        /// The balance slot of `owner` is given by.
        /// ```
        ///     mstore(0x20, ownerSlotSeed)
        ///     mstore(0x00, id)
        ///     let balanceSlot := keccak256(0x00, 0x40)
        /// ```
        ///
        /// The operator approval slot of `owner` is given by.
        /// ```
        ///     mstore(0x20, ownerSlotSeed)
        ///     mstore(0x00, operator)
        ///     let operatorApprovalSlot := keccak256(0x0c, 0x34)
        /// ```
        uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      ERC1155 METADATA                      */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the URI for token `id`.
        ///
        /// You can either return the same templated URI for all token IDs,
        /// (e.g. "https://example.com/api/{id}.json"),
        /// or return a unique URI for each `id`.
        ///
        /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
        function uri(uint256 id) public view virtual returns (string memory);
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          ERC1155                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the amount of `id` owned by `owner`.
        function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                mstore(0x14, owner)
                mstore(0x00, id)
                result := sload(keccak256(0x00, 0x40))
            }
        }
    
        /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
        function isApprovedForAll(address owner, address operator)
            public
            view
            virtual
            returns (bool result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                mstore(0x14, owner)
                mstore(0x00, operator)
                result := sload(keccak256(0x0c, 0x34))
            }
        }
    
        /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
        ///
        /// Emits a {ApprovalForAll} event.
        function setApprovalForAll(address operator, bool isApproved) public virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Convert to 0 or 1.
                isApproved := iszero(iszero(isApproved))
                // Update the `isApproved` for (`msg.sender`, `operator`).
                mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                mstore(0x14, caller())
                mstore(0x00, operator)
                sstore(keccak256(0x0c, 0x34), isApproved)
                // Emit the {ApprovalForAll} event.
                mstore(0x00, isApproved)
                // forgefmt: disable-next-line
                log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
            }
        }
    
        /// @dev Transfers `amount` of `id` from `from` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - `from` must have at least `amount` of `id`.
        /// - If the caller is not `from`,
        ///   it must be approved to manage the tokens of `from`.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferSingle} event.
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) public virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                mstore(0x20, fromSlotSeed)
                // Clear the upper 96 bits.
                from := shr(96, fromSlotSeed)
                to := shr(96, toSlotSeed)
                // Revert if `to` is the zero address.
                if iszero(to) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                // If the caller is not `from`, do the authorization check.
                if iszero(eq(caller(), from)) {
                    mstore(0x00, caller())
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Subtract and store the updated balance of `from`.
                {
                    mstore(0x00, id)
                    let fromBalanceSlot := keccak256(0x00, 0x40)
                    let fromBalance := sload(fromBalanceSlot)
                    if gt(amount, fromBalance) {
                        mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(fromBalanceSlot, sub(fromBalance, amount))
                }
                // Increase and store the updated balance of `to`.
                {
                    mstore(0x20, toSlotSeed)
                    let toBalanceSlot := keccak256(0x00, 0x40)
                    let toBalanceBefore := sload(toBalanceSlot)
                    let toBalanceAfter := add(toBalanceBefore, amount)
                    if lt(toBalanceAfter, toBalanceBefore) {
                        mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(toBalanceSlot, toBalanceAfter)
                }
                // Emit a {TransferSingle} event.
                mstore(0x20, amount)
                log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, to, _single(id), _single(amount), data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                // Do the {onERC1155Received} check if `to` is a smart contract.
                if extcodesize(to) {
                    // Prepare the calldata.
                    let m := mload(0x40)
                    // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                    mstore(m, 0xf23a6e61)
                    mstore(add(m, 0x20), caller())
                    mstore(add(m, 0x40), from)
                    mstore(add(m, 0x60), id)
                    mstore(add(m, 0x80), amount)
                    mstore(add(m, 0xa0), 0xa0)
                    mstore(add(m, 0xc0), data.length)
                    calldatacopy(add(m, 0xe0), data.offset, data.length)
                    // Revert if the call reverts.
                    if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) {
                        if returndatasize() {
                            // Bubble up the revert if the call reverts.
                            returndatacopy(m, 0x00, returndatasize())
                            revert(m, returndatasize())
                        }
                    }
                    // Load the returndata and compare it with the function selector.
                    if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                        mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
    
        /// @dev Transfers `amounts` of `ids` from `from` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - `from` must have at least `amount` of `id`.
        /// - `ids` and `amounts` must have the same length.
        /// - If the caller is not `from`,
        ///   it must be approved to manage the tokens of `from`.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferBatch} event.
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) public virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, to, ids, amounts, data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                if iszero(eq(ids.length, amounts.length)) {
                    mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                    revert(0x1c, 0x04)
                }
                let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                mstore(0x20, fromSlotSeed)
                // Clear the upper 96 bits.
                from := shr(96, fromSlotSeed)
                to := shr(96, toSlotSeed)
                // Revert if `to` is the zero address.
                if iszero(to) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                // If the caller is not `from`, do the authorization check.
                if iszero(eq(caller(), from)) {
                    mstore(0x00, caller())
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Loop through all the `ids` and update the balances.
                {
                    for { let i := shl(5, ids.length) } i {} {
                        i := sub(i, 0x20)
                        let amount := calldataload(add(amounts.offset, i))
                        // Subtract and store the updated balance of `from`.
                        {
                            mstore(0x20, fromSlotSeed)
                            mstore(0x00, calldataload(add(ids.offset, i)))
                            let fromBalanceSlot := keccak256(0x00, 0x40)
                            let fromBalance := sload(fromBalanceSlot)
                            if gt(amount, fromBalance) {
                                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(fromBalanceSlot, sub(fromBalance, amount))
                        }
                        // Increase and store the updated balance of `to`.
                        {
                            mstore(0x20, toSlotSeed)
                            let toBalanceSlot := keccak256(0x00, 0x40)
                            let toBalanceBefore := sload(toBalanceSlot)
                            let toBalanceAfter := add(toBalanceBefore, amount)
                            if lt(toBalanceAfter, toBalanceBefore) {
                                mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(toBalanceSlot, toBalanceAfter)
                        }
                    }
                }
                // Emit a {TransferBatch} event.
                {
                    let m := mload(0x40)
                    // Copy the `ids`.
                    mstore(m, 0x40)
                    let n := shl(5, ids.length)
                    mstore(add(m, 0x40), ids.length)
                    calldatacopy(add(m, 0x60), ids.offset, n)
                    // Copy the `amounts`.
                    mstore(add(m, 0x20), add(0x60, n))
                    let o := add(add(m, n), 0x60)
                    mstore(o, ids.length)
                    calldatacopy(add(o, 0x20), amounts.offset, n)
                    // Do the emit.
                    log4(m, add(add(n, n), 0x80), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to)
                }
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransferCalldata(from, to, ids, amounts, data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                // Do the {onERC1155BatchReceived} check if `to` is a smart contract.
                if extcodesize(to) {
                    mstore(0x00, to) // Cache `to` to prevent stack too deep.
                    let m := mload(0x40)
                    // Prepare the calldata.
                    // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                    mstore(m, 0xbc197c81)
                    mstore(add(m, 0x20), caller())
                    mstore(add(m, 0x40), from)
                    // Copy the `ids`.
                    mstore(add(m, 0x60), 0xa0)
                    let n := shl(5, ids.length)
                    mstore(add(m, 0xc0), ids.length)
                    calldatacopy(add(m, 0xe0), ids.offset, n)
                    // Copy the `amounts`.
                    mstore(add(m, 0x80), add(0xc0, n))
                    let o := add(add(m, n), 0xe0)
                    mstore(o, ids.length)
                    calldatacopy(add(o, 0x20), amounts.offset, n)
                    // Copy the `data`.
                    mstore(add(m, 0xa0), add(add(0xe0, n), n))
                    o := add(add(o, n), 0x20)
                    mstore(o, data.length)
                    calldatacopy(add(o, 0x20), data.offset, data.length)
                    let nAll := add(0x104, add(data.length, add(n, n)))
                    // Revert if the call reverts.
                    if iszero(call(gas(), mload(0x00), 0, add(mload(0x40), 0x1c), nAll, m, 0x20)) {
                        if returndatasize() {
                            // Bubble up the revert if the call reverts.
                            returndatacopy(m, 0x00, returndatasize())
                            revert(m, returndatasize())
                        }
                    }
                    // Load the returndata and compare it with the function selector.
                    if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                        mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
    
        /// @dev Returns the amounts of `ids` for `owners.
        ///
        /// Requirements:
        /// - `owners` and `ids` must have the same length.
        function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
            public
            view
            virtual
            returns (uint256[] memory balances)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if iszero(eq(ids.length, owners.length)) {
                    mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                    revert(0x1c, 0x04)
                }
                balances := mload(0x40)
                mstore(balances, ids.length)
                let o := add(balances, 0x20)
                let i := shl(5, ids.length)
                mstore(0x40, add(i, o))
                // Loop through all the `ids` and load the balances.
                for {} i {} {
                    i := sub(i, 0x20)
                    let owner := calldataload(add(owners.offset, i))
                    mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)))
                    mstore(0x00, calldataload(add(ids.offset, i)))
                    mstore(add(o, i), sload(keccak256(0x00, 0x40)))
                }
            }
        }
    
        /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
        /// See: https://eips.ethereum.org/EIPS/eip-165
        /// This function call must use less than 30000 gas.
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                let s := shr(224, interfaceId)
                // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
                result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  INTERNAL MINT FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Mints `amount` of `id` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferSingle} event.
        function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(address(0), to, _single(id), _single(amount), data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                let to_ := shl(96, to)
                // Revert if `to` is the zero address.
                if iszero(to_) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                // Increase and store the updated balance of `to`.
                {
                    mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                    mstore(0x14, to)
                    mstore(0x00, id)
                    let toBalanceSlot := keccak256(0x00, 0x40)
                    let toBalanceBefore := sload(toBalanceSlot)
                    let toBalanceAfter := add(toBalanceBefore, amount)
                    if lt(toBalanceAfter, toBalanceBefore) {
                        mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(toBalanceSlot, toBalanceAfter)
                }
                // Emit a {TransferSingle} event.
                mstore(0x20, amount)
                log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(address(0), to, _single(id), _single(amount), data);
            }
            if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data);
        }
    
        /// @dev Mints `amounts` of `ids` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - `ids` and `amounts` must have the same length.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferBatch} event.
        function _batchMint(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(address(0), to, ids, amounts, data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                function copy(dst_, src_, n_) {
                    for { let i_ := 0 } lt(i_, n_) { i_ := add(0x20, i_) } {
                        mstore(add(dst_, i_), mload(add(src_, i_)))
                    }
                }
                if iszero(eq(mload(ids), mload(amounts))) {
                    mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                    revert(0x1c, 0x04)
                }
                let to_ := shl(96, to)
                // Revert if `to` is the zero address.
                if iszero(to_) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                // Loop through all the `ids` and update the balances.
                {
                    mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                    for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                        let amount := mload(add(amounts, i))
                        // Increase and store the updated balance of `to`.
                        {
                            mstore(0x00, mload(add(ids, i)))
                            let toBalanceSlot := keccak256(0x00, 0x40)
                            let toBalanceBefore := sload(toBalanceSlot)
                            let toBalanceAfter := add(toBalanceBefore, amount)
                            if lt(toBalanceAfter, toBalanceBefore) {
                                mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(toBalanceSlot, toBalanceAfter)
                        }
                    }
                }
                // Emit a {TransferBatch} event.
                {
                    let m := mload(0x40)
                    // Copy the `ids`.
                    mstore(m, 0x40)
                    let n := add(0x20, shl(5, mload(ids)))
                    let o := add(m, 0x40)
                    copy(o, ids, n)
                    // Copy the `amounts`.
                    mstore(add(m, 0x20), add(0x40, n))
                    o := add(o, n)
                    n := add(0x20, shl(5, mload(amounts)))
                    copy(o, amounts, n)
                    n := sub(add(o, n), m)
                    // Do the emit.
                    log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
                }
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(address(0), to, ids, amounts, data);
            }
            if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data);
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  INTERNAL BURN FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Equivalent to `_burn(address(0), from, id, amount)`.
        function _burn(address from, uint256 id, uint256 amount) internal virtual {
            _burn(address(0), from, id, amount);
        }
    
        /// @dev Destroys `amount` of `id` from `from`.
        ///
        /// Requirements:
        /// - `from` must have at least `amount` of `id`.
        /// - If `by` is not the zero address, it must be either `from`,
        ///   or approved to manage the tokens of `from`.
        ///
        /// Emits a {TransferSingle} event.
        function _burn(address by, address from, uint256 id, uint256 amount) internal virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, address(0), _single(id), _single(amount), "");
            }
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                // If `by` is not the zero address, and not equal to `from`,
                // check if it is approved to manage all the tokens of `from`.
                if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) {
                    mstore(0x00, by)
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Decrease and store the updated balance of `from`.
                {
                    mstore(0x00, id)
                    let fromBalanceSlot := keccak256(0x00, 0x40)
                    let fromBalance := sload(fromBalanceSlot)
                    if gt(amount, fromBalance) {
                        mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(fromBalanceSlot, sub(fromBalance, amount))
                }
                // Emit a {TransferSingle} event.
                mstore(0x20, amount)
                log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, address(0), _single(id), _single(amount), "");
            }
        }
    
        /// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`.
        function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts)
            internal
            virtual
        {
            _batchBurn(address(0), from, ids, amounts);
        }
    
        /// @dev Destroys `amounts` of `ids` from `from`.
        ///
        /// Requirements:
        /// - `ids` and `amounts` must have the same length.
        /// - `from` must have at least `amounts` of `ids`.
        /// - If `by` is not the zero address, it must be either `from`,
        ///   or approved to manage the tokens of `from`.
        ///
        /// Emits a {TransferBatch} event.
        function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts)
            internal
            virtual
        {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, address(0), ids, amounts, "");
            }
            /// @solidity memory-safe-assembly
            assembly {
                function copy(dst_, src_, n_) {
                    for { let i_ := 0 } lt(i_, n_) { i_ := add(0x20, i_) } {
                        mstore(add(dst_, i_), mload(add(src_, i_)))
                    }
                }
                if iszero(eq(mload(ids), mload(amounts))) {
                    mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                    revert(0x1c, 0x04)
                }
                let from_ := shl(96, from)
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                // If `by` is not the zero address, and not equal to `from`,
                // check if it is approved to manage all the tokens of `from`.
                let by_ := shl(96, by)
                if iszero(or(iszero(by_), eq(by_, from_))) {
                    mstore(0x00, by)
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Loop through all the `ids` and update the balances.
                {
                    for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                        let amount := mload(add(amounts, i))
                        // Decrease and store the updated balance of `from`.
                        {
                            mstore(0x00, mload(add(ids, i)))
                            let fromBalanceSlot := keccak256(0x00, 0x40)
                            let fromBalance := sload(fromBalanceSlot)
                            if gt(amount, fromBalance) {
                                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(fromBalanceSlot, sub(fromBalance, amount))
                        }
                    }
                }
                // Emit a {TransferBatch} event.
                {
                    let m := mload(0x40)
                    // Copy the `ids`.
                    mstore(m, 0x40)
                    let n := add(0x20, shl(5, mload(ids)))
                    let o := add(m, 0x40)
                    copy(o, ids, n)
                    // Copy the `amounts`.
                    mstore(add(m, 0x20), add(0x40, n))
                    o := add(o, n)
                    n := add(0x20, shl(5, mload(amounts)))
                    copy(o, amounts, n)
                    n := sub(add(o, n), m)
                    // Do the emit.
                    log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
                }
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, address(0), ids, amounts, "");
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                INTERNAL APPROVAL FUNCTIONS                 */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Approve or remove the `operator` as an operator for `by`,
        /// without authorization checks.
        ///
        /// Emits a {ApprovalForAll} event.
        function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Convert to 0 or 1.
                isApproved := iszero(iszero(isApproved))
                // Update the `isApproved` for (`by`, `operator`).
                mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                mstore(0x14, by)
                mstore(0x00, operator)
                sstore(keccak256(0x0c, 0x34), isApproved)
                // Emit the {ApprovalForAll} event.
                mstore(0x00, isApproved)
                let m := shr(96, not(0))
                log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator))
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                INTERNAL TRANSFER FUNCTIONS                 */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`.
        function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data)
            internal
            virtual
        {
            _safeTransfer(address(0), from, to, id, amount, data);
        }
    
        /// @dev Transfers `amount` of `id` from `from` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - `from` must have at least `amount` of `id`.
        /// - If `by` is not the zero address, it must be either `from`,
        ///   or approved to manage the tokens of `from`.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferSingle} event.
        function _safeTransfer(
            address by,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                let to_ := shl(96, to)
                // Revert if `to` is the zero address.
                if iszero(to_) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                // If `by` is not the zero address, and not equal to `from`,
                // check if it is approved to manage all the tokens of `from`.
                let by_ := shl(96, by)
                if iszero(or(iszero(by_), eq(by_, from_))) {
                    mstore(0x00, by)
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Subtract and store the updated balance of `from`.
                {
                    mstore(0x00, id)
                    let fromBalanceSlot := keccak256(0x00, 0x40)
                    let fromBalance := sload(fromBalanceSlot)
                    if gt(amount, fromBalance) {
                        mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(fromBalanceSlot, sub(fromBalance, amount))
                }
                // Increase and store the updated balance of `to`.
                {
                    mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                    let toBalanceSlot := keccak256(0x00, 0x40)
                    let toBalanceBefore := sload(toBalanceSlot)
                    let toBalanceAfter := add(toBalanceBefore, amount)
                    if lt(toBalanceAfter, toBalanceBefore) {
                        mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    sstore(toBalanceSlot, toBalanceAfter)
                }
                // Emit a {TransferSingle} event.
                mstore(0x20, amount)
                // forgefmt: disable-next-line
                log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, to, _single(id), _single(amount), data);
            }
            if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data);
        }
    
        /// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`.
        function _safeBatchTransfer(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            _safeBatchTransfer(address(0), from, to, ids, amounts, data);
        }
    
        /// @dev Transfers `amounts` of `ids` from `from` to `to`.
        ///
        /// Requirements:
        /// - `to` cannot be the zero address.
        /// - `ids` and `amounts` must have the same length.
        /// - `from` must have at least `amounts` of `ids`.
        /// - If `by` is not the zero address, it must be either `from`,
        ///   or approved to manage the tokens of `from`.
        /// - If `to` refers to a smart contract, it must implement
        ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
        ///
        /// Emits a {TransferBatch} event.
        function _safeBatchTransfer(
            address by,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            if (_useBeforeTokenTransfer()) {
                _beforeTokenTransfer(from, to, ids, amounts, data);
            }
            /// @solidity memory-safe-assembly
            assembly {
                function copy(dst_, src_, n_) {
                    for { let i_ := 0 } lt(i_, n_) { i_ := add(0x20, i_) } {
                        mstore(add(dst_, i_), mload(add(src_, i_)))
                    }
                }
                if iszero(eq(mload(ids), mload(amounts))) {
                    mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                    revert(0x1c, 0x04)
                }
                let from_ := shl(96, from)
                let to_ := shl(96, to)
                // Revert if `to` is the zero address.
                if iszero(to_) {
                    mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                    revert(0x1c, 0x04)
                }
                let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_)
                let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_)
                mstore(0x20, fromSlotSeed)
                // If `by` is not the zero address, and not equal to `from`,
                // check if it is approved to manage all the tokens of `from`.
                let by_ := shl(96, by)
                if iszero(or(iszero(by_), eq(by_, from_))) {
                    mstore(0x00, by)
                    if iszero(sload(keccak256(0x0c, 0x34))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Loop through all the `ids` and update the balances.
                {
                    for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                        let amount := mload(add(amounts, i))
                        // Subtract and store the updated balance of `from`.
                        {
                            mstore(0x20, fromSlotSeed)
                            mstore(0x00, mload(add(ids, i)))
                            let fromBalanceSlot := keccak256(0x00, 0x40)
                            let fromBalance := sload(fromBalanceSlot)
                            if gt(amount, fromBalance) {
                                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(fromBalanceSlot, sub(fromBalance, amount))
                        }
                        // Increase and store the updated balance of `to`.
                        {
                            mstore(0x20, toSlotSeed)
                            let toBalanceSlot := keccak256(0x00, 0x40)
                            let toBalanceBefore := sload(toBalanceSlot)
                            let toBalanceAfter := add(toBalanceBefore, amount)
                            if lt(toBalanceAfter, toBalanceBefore) {
                                mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                revert(0x1c, 0x04)
                            }
                            sstore(toBalanceSlot, toBalanceAfter)
                        }
                    }
                }
                // Emit a {TransferBatch} event.
                {
                    let m := mload(0x40)
                    // Copy the `ids`.
                    mstore(m, 0x40)
                    let n := add(0x20, shl(5, mload(ids)))
                    let o := add(m, 0x40)
                    copy(o, ids, n)
                    // Copy the `amounts`.
                    mstore(add(m, 0x20), add(0x40, n))
                    o := add(o, n)
                    n := add(0x20, shl(5, mload(amounts)))
                    copy(o, amounts, n)
                    n := sub(add(o, n), m)
                    // Do the emit.
                    log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
                }
            }
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, to, ids, amounts, data);
            }
            if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data);
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    HOOKS FOR OVERRIDING                    */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
        /// This is to help the compiler avoid producing dead bytecode.
        function _useBeforeTokenTransfer() internal view virtual returns (bool) {
            return false;
        }
    
        /// @dev Hook that is called before any token transfer.
        /// This includes minting and burning, as well as batched variants.
        ///
        /// The same hook is called on both single and batched variants.
        /// For single transfers, the length of the `id` and `amount` arrays are 1.
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
    
        /// @dev Override this function to return true if `_afterTokenTransfer` is used.
        /// This is to help the compiler avoid producing dead bytecode.
        function _useAfterTokenTransfer() internal view virtual returns (bool) {
            return false;
        }
    
        /// @dev Hook that is called after any token transfer.
        /// This includes minting and burning, as well as batched variants.
        ///
        /// The same hook is called on both single and batched variants.
        /// For single transfers, the length of the `id` and `amount` arrays are 1.
        function _afterTokenTransfer(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      PRIVATE HELPERS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Helper for calling the `_afterTokenTransfer` hook.
        /// This is to help the compiler avoid producing dead bytecode.
        function _afterTokenTransferCalldata(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) private {
            if (_useAfterTokenTransfer()) {
                _afterTokenTransfer(from, to, ids, amounts, data);
            }
        }
    
        /// @dev Returns if `a` has bytecode of non-zero length.
        function _hasCode(address a) private view returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := extcodesize(a) // Can handle dirty upper bits.
            }
        }
    
        /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
        /// Reverts if the target does not support the function correctly.
        function _checkOnERC1155Received(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) private {
            /// @solidity memory-safe-assembly
            assembly {
                function copy(dst_, src_, n_) {
                    for { let i_ := 0 } lt(i_, n_) { i_ := add(0x20, i_) } {
                        mstore(add(dst_, i_), mload(add(src_, i_)))
                    }
                }
                // Prepare the calldata.
                let m := mload(0x40)
                // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                mstore(m, 0xf23a6e61)
                mstore(add(m, 0x20), caller())
                mstore(add(m, 0x40), shr(96, shl(96, from)))
                mstore(add(m, 0x60), id)
                mstore(add(m, 0x80), amount)
                mstore(add(m, 0xa0), 0xa0)
                let n := mload(data)
                mstore(add(m, 0xc0), n)
                copy(add(m, 0xe0), add(data, 0x20), n)
                // Revert if the call reverts.
                if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
                    if returndatasize() {
                        // Bubble up the revert if the call reverts.
                        returndatacopy(m, 0x00, returndatasize())
                        revert(m, returndatasize())
                    }
                }
                // Load the returndata and compare it with the function selector.
                if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                    mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    
        /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
        /// Reverts if the target does not support the function correctly.
        function _checkOnERC1155BatchReceived(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) private {
            /// @solidity memory-safe-assembly
            assembly {
                function copy(dst_, src_, n_) {
                    for { let i_ := 0 } lt(i_, n_) { i_ := add(0x20, i_) } {
                        mstore(add(dst_, i_), mload(add(src_, i_)))
                    }
                }
                // Prepare the calldata.
                let m := mload(0x40)
                // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                mstore(m, 0xbc197c81)
                mstore(add(m, 0x20), caller())
                mstore(add(m, 0x40), shr(96, shl(96, from)))
                // Copy the `ids`.
                mstore(add(m, 0x60), 0xa0)
                let n := add(0x20, shl(5, mload(ids)))
                let o := add(m, 0xc0)
                copy(o, ids, n)
                // Copy the `amounts`.
                let s := add(0xa0, n)
                mstore(add(m, 0x80), s)
                o := add(o, n)
                n := add(0x20, shl(5, mload(amounts)))
                copy(o, amounts, n)
                // Copy the `data`.
                mstore(add(m, 0xa0), add(s, n))
                o := add(o, n)
                n := add(0x20, mload(data))
                copy(o, data, n)
                n := sub(add(o, n), add(m, 0x1c))
                // Revert if the call reverts.
                if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                    if returndatasize() {
                        // Bubble up the revert if the call reverts.
                        returndatacopy(m, 0x00, returndatasize())
                        revert(m, returndatasize())
                    }
                }
                // Load the returndata and compare it with the function selector.
                if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                    mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    
        /// @dev Returns `x` in an array with a single element.
        function _single(uint256 x) private pure returns (uint256[] memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                mstore(0x40, add(result, 0x40))
                mstore(result, 1)
                mstore(add(result, 0x20), x)
            }
        }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Library for byte related operations.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
    library LibBytes {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          STRUCTS                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
        /// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
        /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
        struct BytesStorage {
            bytes32 _spacer;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The constant returned when the `search` is not found in the bytes.
        uint256 internal constant NOT_FOUND = type(uint256).max;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  BYTE STORAGE OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Sets the value of the bytes storage `$` to `s`.
        function set(BytesStorage storage $, bytes memory s) internal {
            /// @solidity memory-safe-assembly
            assembly {
                let n := mload(s)
                let packed := or(0xff, shl(8, n))
                for { let i := 0 } 1 {} {
                    if iszero(gt(n, 0xfe)) {
                        i := 0x1f
                        packed := or(n, shl(8, mload(add(s, i))))
                        if iszero(gt(n, i)) { break }
                    }
                    let o := add(s, 0x20)
                    mstore(0x00, $.slot)
                    for { let p := keccak256(0x00, 0x20) } 1 {} {
                        sstore(add(p, shr(5, i)), mload(add(o, i)))
                        i := add(i, 0x20)
                        if iszero(lt(i, n)) { break }
                    }
                    break
                }
                sstore($.slot, packed)
            }
        }
    
        /// @dev Sets the value of the bytes storage `$` to `s`.
        function setCalldata(BytesStorage storage $, bytes calldata s) internal {
            /// @solidity memory-safe-assembly
            assembly {
                let packed := or(0xff, shl(8, s.length))
                for { let i := 0 } 1 {} {
                    if iszero(gt(s.length, 0xfe)) {
                        i := 0x1f
                        packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
                        if iszero(gt(s.length, i)) { break }
                    }
                    mstore(0x00, $.slot)
                    for { let p := keccak256(0x00, 0x20) } 1 {} {
                        sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
                        i := add(i, 0x20)
                        if iszero(lt(i, s.length)) { break }
                    }
                    break
                }
                sstore($.slot, packed)
            }
        }
    
        /// @dev Sets the value of the bytes storage `$` to the empty bytes.
        function clear(BytesStorage storage $) internal {
            delete $._spacer;
        }
    
        /// @dev Returns whether the value stored is `$` is the empty bytes "".
        function isEmpty(BytesStorage storage $) internal view returns (bool) {
            return uint256($._spacer) & 0xff == uint256(0);
        }
    
        /// @dev Returns the length of the value stored in `$`.
        function length(BytesStorage storage $) internal view returns (uint256 result) {
            result = uint256($._spacer);
            /// @solidity memory-safe-assembly
            assembly {
                let n := and(0xff, result)
                result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
            }
        }
    
        /// @dev Returns the value stored in `$`.
        function get(BytesStorage storage $) internal view returns (bytes memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let o := add(result, 0x20)
                let packed := sload($.slot)
                let n := shr(8, packed)
                for { let i := 0 } 1 {} {
                    if iszero(eq(or(packed, 0xff), packed)) {
                        mstore(o, packed)
                        n := and(0xff, packed)
                        i := 0x1f
                        if iszero(gt(n, i)) { break }
                    }
                    mstore(0x00, $.slot)
                    for { let p := keccak256(0x00, 0x20) } 1 {} {
                        mstore(add(o, i), sload(add(p, shr(5, i))))
                        i := add(i, 0x20)
                        if iszero(lt(i, n)) { break }
                    }
                    break
                }
                mstore(result, n) // Store the length of the memory.
                mstore(add(o, n), 0) // Zeroize the slot after the bytes.
                mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      BYTES OPERATIONS                      */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
        function replace(bytes memory subject, bytes memory needle, bytes memory replacement)
            internal
            pure
            returns (bytes memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let needleLen := mload(needle)
                let replacementLen := mload(replacement)
                let d := sub(result, subject) // Memory difference.
                let i := add(subject, 0x20) // Subject bytes pointer.
                mstore(0x00, add(i, mload(subject))) // End of subject.
                if iszero(gt(needleLen, mload(subject))) {
                    let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)
                    let h := 0 // The hash of `needle`.
                    if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
                    let s := mload(add(needle, 0x20))
                    for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
                        let t := mload(i)
                        // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
                        if iszero(shr(m, xor(t, s))) {
                            if h {
                                if iszero(eq(keccak256(i, needleLen), h)) {
                                    mstore(add(i, d), t)
                                    i := add(i, 1)
                                    if iszero(lt(i, subjectSearchEnd)) { break }
                                    continue
                                }
                            }
                            // Copy the `replacement` one word at a time.
                            for { let j := 0 } 1 {} {
                                mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
                                j := add(j, 0x20)
                                if iszero(lt(j, replacementLen)) { break }
                            }
                            d := sub(add(d, replacementLen), needleLen)
                            if needleLen {
                                i := add(i, needleLen)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        mstore(add(i, d), t)
                        i := add(i, 1)
                        if iszero(lt(i, subjectSearchEnd)) { break }
                    }
                }
                let end := mload(0x00)
                let n := add(sub(d, add(result, 0x20)), end)
                // Copy the rest of the bytes one word at a time.
                for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
                let o := add(i, d)
                mstore(o, 0) // Zeroize the slot after the bytes.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
                mstore(result, n) // Store the length.
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from left to right, starting from `from`.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function indexOf(bytes memory subject, bytes memory needle, uint256 from)
            internal
            pure
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := not(0) // Initialize to `NOT_FOUND`.
                for { let subjectLen := mload(subject) } 1 {} {
                    if iszero(mload(needle)) {
                        result := from
                        if iszero(gt(from, subjectLen)) { break }
                        result := subjectLen
                        break
                    }
                    let needleLen := mload(needle)
                    let subjectStart := add(subject, 0x20)
    
                    subject := add(subjectStart, from)
                    let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
                    let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
                    let s := mload(add(needle, 0x20))
    
                    if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
    
                    if iszero(lt(needleLen, 0x20)) {
                        for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                            if iszero(shr(m, xor(mload(subject), s))) {
                                if eq(keccak256(subject, needleLen), h) {
                                    result := sub(subject, subjectStart)
                                    break
                                }
                            }
                            subject := add(subject, 1)
                            if iszero(lt(subject, end)) { break }
                        }
                        break
                    }
                    for {} 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            result := sub(subject, subjectStart)
                            break
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from left to right.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {
            return indexOf(subject, needle, 0);
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from right to left, starting from `from`.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from)
            internal
            pure
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for {} 1 {} {
                    result := not(0) // Initialize to `NOT_FOUND`.
                    let needleLen := mload(needle)
                    if gt(needleLen, mload(subject)) { break }
                    let w := result
    
                    let fromMax := sub(mload(subject), needleLen)
                    if iszero(gt(fromMax, from)) { from := fromMax }
    
                    let end := add(add(subject, 0x20), w)
                    subject := add(add(subject, 0x20), from)
                    if iszero(gt(subject, end)) { break }
                    // As this function is not too often used,
                    // we shall simply use keccak256 for smaller bytecode size.
                    for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                        if eq(keccak256(subject, needleLen), h) {
                            result := sub(subject, add(end, 1))
                            break
                        }
                        subject := add(subject, w) // `sub(subject, 1)`.
                        if iszero(gt(subject, end)) { break }
                    }
                    break
                }
            }
        }
    
        /// @dev Returns the byte index of the first location of `needle` in `subject`,
        /// needleing from right to left.
        /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
        function lastIndexOf(bytes memory subject, bytes memory needle)
            internal
            pure
            returns (uint256)
        {
            return lastIndexOf(subject, needle, type(uint256).max);
        }
    
        /// @dev Returns true if `needle` is found in `subject`, false otherwise.
        function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {
            return indexOf(subject, needle) != NOT_FOUND;
        }
    
        /// @dev Returns whether `subject` starts with `needle`.
        function startsWith(bytes memory subject, bytes memory needle)
            internal
            pure
            returns (bool result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let n := mload(needle)
                // Just using keccak256 directly is actually cheaper.
                let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
                result := lt(gt(n, mload(subject)), t)
            }
        }
    
        /// @dev Returns whether `subject` ends with `needle`.
        function endsWith(bytes memory subject, bytes memory needle)
            internal
            pure
            returns (bool result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let n := mload(needle)
                let notInRange := gt(n, mload(subject))
                // `subject + 0x20 + max(subject.length - needle.length, 0)`.
                let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
                // Just using keccak256 directly is actually cheaper.
                result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
            }
        }
    
        /// @dev Returns `subject` repeated `times`.
        function repeat(bytes memory subject, uint256 times)
            internal
            pure
            returns (bytes memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let l := mload(subject) // Subject length.
                if iszero(or(iszero(times), iszero(l))) {
                    result := mload(0x40)
                    subject := add(subject, 0x20)
                    let o := add(result, 0x20)
                    for {} 1 {} {
                        // Copy the `subject` one word at a time.
                        for { let j := 0 } 1 {} {
                            mstore(add(o, j), mload(add(subject, j)))
                            j := add(j, 0x20)
                            if iszero(lt(j, l)) { break }
                        }
                        o := add(o, l)
                        times := sub(times, 1)
                        if iszero(times) { break }
                    }
                    mstore(o, 0) // Zeroize the slot after the bytes.
                    mstore(0x40, add(o, 0x20)) // Allocate memory.
                    mstore(result, sub(o, add(result, 0x20))) // Store the length.
                }
            }
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
        /// `start` and `end` are byte offsets.
        function slice(bytes memory subject, uint256 start, uint256 end)
            internal
            pure
            returns (bytes memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let l := mload(subject) // Subject length.
                if iszero(gt(l, end)) { end := l }
                if iszero(gt(l, start)) { start := l }
                if lt(start, end) {
                    result := mload(0x40)
                    let n := sub(end, start)
                    let i := add(subject, start)
                    let w := not(0x1f)
                    // Copy the `subject` one word at a time, backwards.
                    for { let j := and(add(n, 0x1f), w) } 1 {} {
                        mstore(add(result, j), mload(add(i, j)))
                        j := add(j, w) // `sub(j, 0x20)`.
                        if iszero(j) { break }
                    }
                    let o := add(add(result, 0x20), n)
                    mstore(o, 0) // Zeroize the slot after the bytes.
                    mstore(0x40, add(o, 0x20)) // Allocate memory.
                    mstore(result, n) // Store the length.
                }
            }
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
        /// `start` is a byte offset.
        function slice(bytes memory subject, uint256 start)
            internal
            pure
            returns (bytes memory result)
        {
            result = slice(subject, start, type(uint256).max);
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
        /// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
        function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
            internal
            pure
            returns (bytes calldata result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
                start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
                result.offset := add(subject.offset, start)
                result.length := mul(lt(start, end), sub(end, start))
            }
        }
    
        /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
        /// `start` is a byte offset. Faster than Solidity's native slicing.
        function sliceCalldata(bytes calldata subject, uint256 start)
            internal
            pure
            returns (bytes calldata result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
                result.offset := add(subject.offset, start)
                result.length := mul(lt(start, subject.length), sub(subject.length, start))
            }
        }
    
        /// @dev Reduces the size of `subject` to `n`.
        /// If `n` is greater than the size of `subject`, this will be a no-op.
        function truncate(bytes memory subject, uint256 n)
            internal
            pure
            returns (bytes memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := subject
                mstore(mul(lt(n, mload(result)), result), n)
            }
        }
    
        /// @dev Returns a copy of `subject`, with the length reduced to `n`.
        /// If `n` is greater than the size of `subject`, this will be a no-op.
        function truncatedCalldata(bytes calldata subject, uint256 n)
            internal
            pure
            returns (bytes calldata result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result.offset := subject.offset
                result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
            }
        }
    
        /// @dev Returns all the indices of `needle` in `subject`.
        /// The indices are byte offsets.
        function indicesOf(bytes memory subject, bytes memory needle)
            internal
            pure
            returns (uint256[] memory result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let searchLen := mload(needle)
                if iszero(gt(searchLen, mload(subject))) {
                    result := mload(0x40)
                    let i := add(subject, 0x20)
                    let o := add(result, 0x20)
                    let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
                    let h := 0 // The hash of `needle`.
                    if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
                    let s := mload(add(needle, 0x20))
                    for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
                        let t := mload(i)
                        // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
                        if iszero(shr(m, xor(t, s))) {
                            if h {
                                if iszero(eq(keccak256(i, searchLen), h)) {
                                    i := add(i, 1)
                                    if iszero(lt(i, subjectSearchEnd)) { break }
                                    continue
                                }
                            }
                            mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
                            o := add(o, 0x20)
                            i := add(i, searchLen) // Advance `i` by `searchLen`.
                            if searchLen {
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        i := add(i, 1)
                        if iszero(lt(i, subjectSearchEnd)) { break }
                    }
                    mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
                    // Allocate memory for result.
                    // We allocate one more word, so this array can be recycled for {split}.
                    mstore(0x40, add(o, 0x20))
                }
            }
        }
    
        /// @dev Returns a arrays of bytess based on the `delimiter` inside of the `subject` bytes.
        function split(bytes memory subject, bytes memory delimiter)
            internal
            pure
            returns (bytes[] memory result)
        {
            uint256[] memory indices = indicesOf(subject, delimiter);
            /// @solidity memory-safe-assembly
            assembly {
                let w := not(0x1f)
                let indexPtr := add(indices, 0x20)
                let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                mstore(add(indicesEnd, w), mload(subject))
                mstore(indices, add(mload(indices), 1))
                for { let prevIndex := 0 } 1 {} {
                    let index := mload(indexPtr)
                    mstore(indexPtr, 0x60)
                    if iszero(eq(index, prevIndex)) {
                        let element := mload(0x40)
                        let l := sub(index, prevIndex)
                        mstore(element, l) // Store the length of the element.
                        // Copy the `subject` one word at a time, backwards.
                        for { let o := and(add(l, 0x1f), w) } 1 {} {
                            mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                            o := add(o, w) // `sub(o, 0x20)`.
                            if iszero(o) { break }
                        }
                        mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.
                        // Allocate memory for the length and the bytes, rounded up to a multiple of 32.
                        mstore(0x40, add(element, and(add(l, 0x3f), w)))
                        mstore(indexPtr, element) // Store the `element` into the array.
                    }
                    prevIndex := add(index, mload(delimiter))
                    indexPtr := add(indexPtr, 0x20)
                    if iszero(lt(indexPtr, indicesEnd)) { break }
                }
                result := indices
                if iszero(mload(delimiter)) {
                    result := add(indices, 0x20)
                    mstore(result, sub(mload(indices), 2))
                }
            }
        }
    
        /// @dev Returns a concatenated bytes of `a` and `b`.
        /// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.
        function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                let w := not(0x1f)
                let aLen := mload(a)
                // Copy `a` one word at a time, backwards.
                for { let o := and(add(aLen, 0x20), w) } 1 {} {
                    mstore(add(result, o), mload(add(a, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                let bLen := mload(b)
                let output := add(result, aLen)
                // Copy `b` one word at a time, backwards.
                for { let o := and(add(bLen, 0x20), w) } 1 {} {
                    mstore(add(output, o), mload(add(b, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                let totalLen := add(aLen, bLen)
                let last := add(add(result, 0x20), totalLen)
                mstore(last, 0) // Zeroize the slot after the bytes.
                mstore(result, totalLen) // Store the length.
                mstore(0x40, add(last, 0x20)) // Allocate memory.
            }
        }
    
        /// @dev Returns whether `a` equals `b`.
        function eq(bytes memory a, bytes memory b) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
            }
        }
    
        /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes.
        function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) {
            /// @solidity memory-safe-assembly
            assembly {
                // These should be evaluated on compile time, as far as possible.
                let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
                let x := not(or(m, or(b, add(m, and(b, m)))))
                let r := shl(7, iszero(iszero(shr(128, x))))
                r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                r := or(r, shl(4, lt(0xffff, shr(r, x))))
                r := or(r, shl(3, lt(0xff, shr(r, x))))
                // forgefmt: disable-next-item
                result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                    xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
            }
        }
    
        /// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
        /// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
        function cmp(bytes memory a, bytes memory b) internal pure returns (int256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let aLen := mload(a)
                let bLen := mload(b)
                let n := and(xor(aLen, mul(xor(aLen, bLen), lt(bLen, aLen))), not(0x1f))
                if n {
                    for { let i := 0x20 } 1 {} {
                        let x := mload(add(a, i))
                        let y := mload(add(b, i))
                        if iszero(or(xor(x, y), eq(i, n))) {
                            i := add(i, 0x20)
                            continue
                        }
                        result := sub(gt(x, y), lt(x, y))
                        break
                    }
                }
                // forgefmt: disable-next-item
                if iszero(result) {
                    let l := 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
                    let x := and(mload(add(add(a, 0x20), n)), shl(shl(3, byte(sub(aLen, n), l)), not(0)))
                    let y := and(mload(add(add(b, 0x20), n)), shl(shl(3, byte(sub(bLen, n), l)), not(0)))
                    result := sub(gt(x, y), lt(x, y))
                    if iszero(result) { result := sub(gt(aLen, bLen), lt(aLen, bLen)) }
                }
            }
        }
    
        /// @dev Directly returns `a` without copying.
        function directReturn(bytes memory a) internal pure {
            assembly {
                // Assumes that the bytes does not start from the scratch space.
                let retStart := sub(a, 0x20)
                let retUnpaddedSize := add(mload(a), 0x40)
                // Right pad with zeroes. Just in case the bytes is produced
                // by a method that doesn't zero right pad.
                mstore(add(retStart, retUnpaddedSize), 0)
                mstore(retStart, 0x20) // Store the return offset.
                // End the transaction, returning the bytes.
                return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
            }
        }
    
        /// @dev Directly returns `a` with minimal copying.
        function directReturn(bytes[] memory a) internal pure {
            assembly {
                let n := mload(a) // `a.length`.
                let o := add(a, 0x20) // Start of elements in `a`.
                let u := a // Highest memory slot.
                let w := not(0x1f)
                for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
                    let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.
                    let s := mload(c) // `a[i]`.
                    let l := mload(s) // `a[i].length`.
                    let r := and(l, 0x1f) // `a[i].length % 32`.
                    let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.
                    // If `s` comes before `o`, or `s` is not zero right padded.
                    if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {
                        let m := mload(0x40)
                        mstore(m, l) // Copy `a[i].length`.
                        for {} 1 {} {
                            mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.
                            z := add(z, w) // `sub(z, 0x20)`.
                            if iszero(z) { break }
                        }
                        let e := add(add(m, 0x20), l)
                        mstore(e, 0) // Zeroize the slot after the copied bytes.
                        mstore(0x40, add(e, 0x20)) // Allocate memory.
                        s := m
                    }
                    mstore(c, sub(s, o)) // Convert to calldata offset.
                    let t := add(l, add(s, 0x20))
                    if iszero(lt(t, u)) { u := t }
                }
                let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.
                mstore(retStart, 0x20) // Store the return offset.
                return(retStart, add(0x40, sub(u, retStart))) // End the transaction.
            }
        }
    
        /// @dev Returns the word at `offset`, without any bounds checks.
        /// To load an address, you can use `address(bytes20(load(a, offset)))`.
        function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(add(add(a, 0x20), offset))
            }
        }
    
        /// @dev Returns the word at `offset`, without any bounds checks.
        /// To load an address, you can use `address(bytes20(loadCalldata(a, offset)))`.
        function loadCalldata(bytes calldata a, uint256 offset)
            internal
            pure
            returns (bytes32 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := calldataload(add(a.offset, offset))
            }
        }
    
        /// @dev Returns empty calldata bytes. For silencing the compiler.
        function emptyCalldata() internal pure returns (bytes calldata result) {
            /// @solidity memory-safe-assembly
            assembly {
                result.length := 0
            }
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    
    /**
     * @dev Required interface of an ERC-1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[ERC].
     */
    interface IERC1155 is IERC165 {
        /**
         * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
         */
        event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    
        /**
         * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
         * transfers.
         */
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] values
        );
    
        /**
         * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
         * `approved`.
         */
        event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    
        /**
         * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
         *
         * If an {URI} event was emitted for `id`, the standard
         * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
         * returned by {IERC1155MetadataURI-uri}.
         */
        event URI(string value, uint256 indexed id);
    
        /**
         * @dev Returns the value of tokens of token type `id` owned by `account`.
         */
        function balanceOf(address account, uint256 id) external view returns (uint256);
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(
            address[] calldata accounts,
            uint256[] calldata ids
        ) external view returns (uint256[] memory);
    
        /**
         * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the zero address.
         */
        function setApprovalForAll(address operator, bool approved) external;
    
        /**
         * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address account, address operator) external view returns (bool);
    
        /**
         * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
         *
         * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
         * to an untrusted contract, when invoking {onERC1155Received} on the receiver.
         * Ensure to follow the checks-effects-interactions pattern and consider employing
         * reentrancy guards when interacting with untrusted contracts.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `value` amount.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
         * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
         * Ensure to follow the checks-effects-interactions pattern and consider employing
         * reentrancy guards when interacting with untrusted contracts.
         *
         * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
         *
         * Requirements:
         *
         * - `ids` and `values` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external;
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC1155} from "../IERC1155.sol";
    
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[ERC].
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\{id\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }

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

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

    Context size (optional):