ETH Price: $2,450.64 (-1.06%)

Transaction Decoder

Block:
21369575 at Dec-10-2024 03:59:11 AM +UTC
Transaction Fee:
0.001285316994874083 ETH $3.15
Gas Used:
56,607 Gas / 22.705972669 Gwei

Emitted Events:

243 Mint.TransferSingle( operator=[Sender] 0x0ae3a29e3694abd00faca7172344557912bdd9ae, from=0x00000000...000000000, to=[Sender] 0x0ae3a29e3694abd00faca7172344557912bdd9ae, id=4, value=1 )
244 Mint.NewMint( tokenId=4, unitPrice=1245958360140000, amount=1, minter=[Sender] 0x0ae3a29e3694abd00faca7172344557912bdd9ae )

Account State Difference:

  Address   Before After State Difference Code
0x0AE3A29E...912bDD9Ae
0.098772410908007264 Eth
Nonce: 544
0.096209899650418181 Eth
Nonce: 545
0.002562511257589083
0x3dcC0af8...2A9d5C3dd 0.511368442754205 Eth0.51264563701692 Eth0.001277194262715
(Titan Builder)
13.873484012742935634 Eth13.873593830322935634 Eth0.00010981758

Execution Trace

ETH 0.001277194262715 Mint.mint( tokenId=4, amount=1 )
  • ETH 0.001277194262715 Mint.mint( tokenId=4, amount=1 )
    File 1 of 2: Mint
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {Ownable} from "./Ownable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2Step is Ownable {
        address private _pendingOwner;
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            return _pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            _pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            delete _pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard ERC20 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
     */
    interface IERC20Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC20InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC20InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC20InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC20InvalidSpender(address spender);
    }
    /**
     * @dev Standard ERC721 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
     */
    interface IERC721Errors {
        /**
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
         * Used in balance queries.
         * @param owner Address of the current owner of a token.
         */
        error ERC721InvalidOwner(address owner);
        /**
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
         * @param tokenId Identifier number of a token.
         */
        error ERC721NonexistentToken(uint256 tokenId);
        /**
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param tokenId Identifier number of a token.
         * @param owner Address of the current owner of a token.
         */
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC721InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC721InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param tokenId Identifier number of a token.
         */
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC721InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC721InvalidOperator(address operator);
    }
    /**
     * @dev Standard ERC1155 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
     */
    interface IERC1155Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         * @param tokenId Identifier number of a token.
         */
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC1155InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC1155InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param owner Address of the current owner of a token.
         */
        error ERC1155MissingApprovalForAll(address operator, address owner);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC1155InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC1155InvalidOperator(address operator);
        /**
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
         * Used in batch transfers.
         * @param idsLength Length of the array of token identifiers
         * @param valuesLength Length of the array of token amounts
         */
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.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[EIP].
     */
    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.0.1) (token/ERC1155/IERC1155.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     */
    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`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        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 caller.
         */
        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.0.0) (token/ERC1155/IERC1155Receiver.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    /**
     * @dev Interface that must be implemented by smart contracts in order to receive
     * ERC-1155 token transfers.
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
    pragma solidity ^0.8.20;
    import {StorageSlot} from "./StorageSlot.sol";
    import {Math} from "./math/Math.sol";
    /**
     * @dev Collection of functions related to array types.
     */
    library Arrays {
        using StorageSlot for bytes32;
        /**
         * @dev Searches a sorted `array` and returns the first index that contains
         * a value greater or equal to `element`. If no such index exists (i.e. all
         * values in the array are strictly less than `element`), the array length is
         * returned. Time complexity O(log n).
         *
         * `array` is expected to be sorted in ascending order, and to contain no
         * repeated elements.
         */
        function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
            uint256 low = 0;
            uint256 high = array.length;
            if (high == 0) {
                return 0;
            }
            while (low < high) {
                uint256 mid = Math.average(low, high);
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
                // because Math.average rounds towards zero (it does integer division with truncation).
                if (unsafeAccess(array, mid).value > element) {
                    high = mid;
                } else {
                    low = mid + 1;
                }
            }
            // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
            if (low > 0 && unsafeAccess(array, low - 1).value == element) {
                return low - 1;
            } else {
                return low;
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getAddressSlot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getBytes32Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getUint256Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides a set of functions to operate with Base64 strings.
     */
    library Base64 {
        /**
         * @dev Base64 Encoding/Decoding Table
         */
        string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        /**
         * @dev Converts a `bytes` to its Bytes64 `string` representation.
         */
        function encode(bytes memory data) internal pure returns (string memory) {
            /**
             * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
             * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
             */
            if (data.length == 0) return "";
            // Loads the table into memory
            string memory table = _TABLE;
            // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
            // and split into 4 numbers of 6 bits.
            // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
            // - `data.length + 2`  -> Round up
            // - `/ 3`              -> Number of 3-bytes chunks
            // - `4 *`              -> 4 characters for each chunk
            string memory result = new string(4 * ((data.length + 2) / 3));
            /// @solidity memory-safe-assembly
            assembly {
                // Prepare the lookup table (skip the first "length" byte)
                let tablePtr := add(table, 1)
                // Prepare result pointer, jump over length
                let resultPtr := add(result, 0x20)
                let dataPtr := data
                let endPtr := add(data, mload(data))
                // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
                // set it to zero to make sure no dirty bytes are read in that section.
                let afterPtr := add(endPtr, 0x20)
                let afterCache := mload(afterPtr)
                mstore(afterPtr, 0x00)
                // Run over the input, 3 bytes at a time
                for {
                } lt(dataPtr, endPtr) {
                } {
                    // Advance 3 bytes
                    dataPtr := add(dataPtr, 3)
                    let input := mload(dataPtr)
                    // To write each character, shift the 3 byte (24 bits) chunk
                    // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                    // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                    // Use this as an index into the lookup table, mload an entire word
                    // so the desired character is in the least significant byte, and
                    // mstore8 this least significant byte into the result and continue.
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                }
                // Reset the value that was cached
                mstore(afterPtr, afterCache)
                // When data `bytes` is not exactly 3 bytes long
                // it is padded with `=` characters at the end
                switch mod(mload(data), 3)
                case 1 {
                    mstore8(sub(resultPtr, 1), 0x3d)
                    mstore8(sub(resultPtr, 2), 0x3d)
                }
                case 2 {
                    mstore8(sub(resultPtr, 1), 0x3d)
                }
            }
            return result;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
         * denominator == 0.
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
         * Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                uint256 twos = denominator & (0 - denominator);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                // works in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
         * towards zero.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard signed math utilities missing in the Solidity language.
     */
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```solidity
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(newImplementation.code.length > 0);
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
        struct BooleanSlot {
            bool value;
        }
        struct Bytes32Slot {
            bytes32 value;
        }
        struct Uint256Slot {
            uint256 value;
        }
        struct StringSlot {
            string value;
        }
        struct BytesSlot {
            bytes value;
        }
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` with member `value` located at `slot`.
         */
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
         */
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` with member `value` located at `slot`.
         */
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
         */
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
    pragma solidity ^0.8.20;
    import {Math} from "./math/Math.sol";
    import {SignedMath} from "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
        uint8 private constant ADDRESS_LENGTH = 20;
        /**
         * @dev The `value` string doesn't fit in the specified `length`.
         */
        error StringsInsufficientHexLength(uint256 value, uint256 length);
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toStringSigned(int256 value) internal pure returns (string memory) {
            return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            uint256 localValue = value;
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_DIGITS[localValue & 0xf];
                localValue >>= 4;
            }
            if (localValue != 0) {
                revert StringsInsufficientHexLength(value, length);
            }
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
         * representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
        }
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol";
    import { IERC1155              } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    import { IERC1155Receiver      } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
    import { IERC1155MetadataURI   } from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
    import { Context               } from "@openzeppelin/contracts/utils/Context.sol";
    import { IERC165, ERC165       } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    import { Arrays                } from "@openzeppelin/contracts/utils/Arrays.sol";
    import { IERC1155Errors        } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    /**
     * @dev Minimal implementation of the basic standard multi-token based on the OpenZeppelin contracts.
     * See https://eips.ethereum.org/EIPS/eip-1155
     */
    abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors, Ownable2Step {
        using Arrays for uint256[];
        using Arrays for address[];
        mapping(uint256 id => mapping(address account => uint256)) private _balances;
        mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
        constructor() Ownable(msg.sender) {}
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256 /* id */) external view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC1155-balanceOf}.
         */
        function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
            return _balances[id][account];
        }
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(
            address[] memory accounts,
            uint256[] memory ids
        ) public view virtual returns (uint256[] memory) {
            if (accounts.length != ids.length) {
                revert ERC1155InvalidArrayLength(ids.length, accounts.length);
            }
            uint256[] memory batchBalances = new uint256[](accounts.length);
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
            }
            return batchBalances;
        }
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
            return _operatorApprovals[account][operator];
        }
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
            address sender = _msgSender();
            if (from != sender && !isApprovedForAll(from, sender)) {
                revert ERC1155MissingApprovalForAll(sender, from);
            }
            _safeTransferFrom(from, to, id, value, data);
        }
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) public virtual {
            address sender = _msgSender();
            if (from != sender && !isApprovedForAll(from, sender)) {
                revert ERC1155MissingApprovalForAll(sender, from);
            }
            _safeBatchTransferFrom(from, to, ids, values, data);
        }
        /**
         * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
         * (or `to`) is the zero address.
         *
         * Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
         *   or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
         * - `ids` and `values` must have the same length.
         *
         * NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
         */
        function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
            if (ids.length != values.length) {
                revert ERC1155InvalidArrayLength(ids.length, values.length);
            }
            address operator = _msgSender();
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids.unsafeMemoryAccess(i);
                uint256 value = values.unsafeMemoryAccess(i);
                if (from != address(0)) {
                    uint256 fromBalance = _balances[id][from];
                    if (fromBalance < value) {
                        revert ERC1155InsufficientBalance(from, fromBalance, value, id);
                    }
                    unchecked {
                        // Overflow not possible: value <= fromBalance
                        _balances[id][from] = fromBalance - value;
                    }
                }
                if (to != address(0)) {
                    _balances[id][to] += value;
                }
            }
            if (ids.length == 1) {
                uint256 id = ids.unsafeMemoryAccess(0);
                uint256 value = values.unsafeMemoryAccess(0);
                emit TransferSingle(operator, from, to, id, value);
            } else {
                emit TransferBatch(operator, from, to, ids, values);
            }
        }
        /**
         * @dev Version of {_update} that performs the token acceptance check by calling
         * {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
         * contains code (eg. is a smart contract at the moment of execution).
         *
         * IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
         * update to the contract state after this function would break the check-effect-interaction pattern. Consider
         * overriding {_update} instead.
         */
        function _updateWithAcceptanceCheck(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) internal virtual {
            _update(from, to, ids, values);
            if (to != address(0)) {
                address operator = _msgSender();
                if (ids.length == 1) {
                    uint256 id = ids.unsafeMemoryAccess(0);
                    uint256 value = values.unsafeMemoryAccess(0);
                    _doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
                } else {
                    _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
                }
            }
        }
        /**
         * @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `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 memory data) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(from, to, ids, values, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         * - `ids` and `values` must have the same length.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            _updateWithAcceptanceCheck(from, to, ids, values, data);
        }
        /**
         * @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(address(0), to, ids, values, data);
        }
        /**
         * @dev Destroys a `value` amount of tokens of type `id` from `from`
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `from` must have at least `value` amount of tokens of type `id`.
         */
        function _burn(address from, uint256 id, uint256 value) internal {
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(from, address(0), ids, values, "");
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the zero address.
         */
        function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
            if (operator == address(0)) {
                revert ERC1155InvalidOperator(address(0));
            }
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
         * if it contains code at the moment of execution.
         */
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 value,
            bytes memory data
        ) private {
            if (to.code.length > 0) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        // Tokens rejected
                        revert ERC1155InvalidReceiver(to);
                    }
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        // non-ERC1155Receiver implementer
                        revert ERC1155InvalidReceiver(to);
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            }
        }
        /**
         * @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
         * if it contains code at the moment of execution.
         */
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) private {
            if (to.code.length > 0) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        // Tokens rejected
                        revert ERC1155InvalidReceiver(to);
                    }
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        // non-ERC1155Receiver implementer
                        revert ERC1155InvalidReceiver(to);
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            }
        }
        /**
         * @dev Creates an array in memory with only one value for each of the elements provided.
         */
        function _asSingletonArrays(
            uint256 element1,
            uint256 element2
        ) private pure returns (uint256[] memory array1, uint256[] memory array2) {
            /// @solidity memory-safe-assembly
            assembly {
                // Load the free memory pointer
                array1 := mload(0x40)
                // Set array length to 1
                mstore(array1, 1)
                // Store the single element at the next word after the length (where content starts)
                mstore(add(array1, 0x20), element1)
                // Repeat for next array locating it right after the first array
                array2 := add(array1, 0x40)
                mstore(array2, 1)
                mstore(add(array2, 0x20), element2)
                // Update the free memory pointer by pointing after the second array
                mstore(0x40, add(array2, 0x40))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Token } from "../types/Token.sol";
    interface IRenderer {
        function name () external pure returns (string memory);
        function version () external pure returns (uint version);
        function uri (uint tokenId, Token calldata token) external view returns (string memory);
        function imageURI (uint tokenId, Token calldata token) external view returns (string memory);
        function animationURI (uint tokenId, Token calldata token) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
    import { Base64  } from "@openzeppelin/contracts/utils/Base64.sol";
    import { SSTORE2 } from "./SSTORE2.sol";
    library ContractMetadata {
        struct Data {
            string name;
            string symbol;
            string description;
            address[] image;
        }
        function uri (Data memory data) external view returns (string memory) {
            bytes memory dataURI = abi.encodePacked(
                '{',
                    '"name": "', data.name, '",',
                    '"symbol": "', data.symbol, '",',
                    '"description": "', data.description, '",',
                    '"image": "', image(data), '"',
                '}'
            );
            return string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(dataURI)
                )
            );
        }
        function image (Data memory data) internal view returns (bytes memory content) {
            for (uint8 i = 0; i < data.image.length; i++) {
                content = abi.encodePacked(content, SSTORE2.read(data.image[i]));
            }
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Read and write to persistent storage at a fraction of the cost.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
    /// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
    library SSTORE2 {
        uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.
        /*//////////////////////////////////////////////////////////////
                                   WRITE LOGIC
        //////////////////////////////////////////////////////////////*/
        function write(bytes memory data) internal returns (address pointer) {
            // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
            bytes memory runtimeCode = abi.encodePacked(hex"00", data);
            bytes memory creationCode = abi.encodePacked(
                //---------------------------------------------------------------------------------------------------------------//
                // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
                //---------------------------------------------------------------------------------------------------------------//
                // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
                // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
                // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
                // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
                // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
                // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
                // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
                // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
                // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
                // 0xf3    |  0xf3               | RETURN       |                                                                //
                //---------------------------------------------------------------------------------------------------------------//
                hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
                runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
            );
            /// @solidity memory-safe-assembly
            assembly {
                // Deploy a new contract with the generated creation code.
                // We start 32 bytes into the code to avoid copying the byte length.
                pointer := create(0, add(creationCode, 32), mload(creationCode))
            }
            require(pointer != address(0), "DEPLOYMENT_FAILED");
        }
        /*//////////////////////////////////////////////////////////////
                                   READ LOGIC
        //////////////////////////////////////////////////////////////*/
        function read(address pointer) internal view returns (bytes memory) {
            return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
        }
        function read(address pointer, uint256 start) internal view returns (bytes memory) {
            start += DATA_OFFSET;
            return readBytecode(pointer, start, pointer.code.length - start);
        }
        function read(
            address pointer,
            uint256 start,
            uint256 end
        ) internal view returns (bytes memory) {
            start += DATA_OFFSET;
            end += DATA_OFFSET;
            require(pointer.code.length >= end, "OUT_OF_BOUNDS");
            return readBytecode(pointer, start, end - start);
        }
        /*//////////////////////////////////////////////////////////////
                              INTERNAL HELPER LOGIC
        //////////////////////////////////////////////////////////////*/
        function readBytecode(
            address pointer,
            uint256 start,
            uint256 size
        ) private view returns (bytes memory data) {
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                data := mload(0x40)
                // Update the free memory pointer to prevent overriding our data.
                // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
                // Adding 31 to size and running the result through the logic above ensures
                // the memory pointer remains word-aligned, following the Solidity convention.
                mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
                // Store the size of the data in the first 32 byte chunk of free memory.
                mstore(data, size)
                // Copy the code into memory right after the 32 bytes we used to store the size.
                extcodecopy(pointer, add(data, 32), start, size)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { ERC1155               } from "./ERC1155.sol";
    import { IRenderer             } from "./interfaces/IRenderer.sol";
    import { ContractMetadata      } from "./libraries/ContractMetadata.sol";
    import { SSTORE2               } from "./libraries/SSTORE2.sol";
    import { Token                 } from "./types/Token.sol";
    /// @notice To mint is a human right.
    /// @author Visualize Value
    contract Mint is ERC1155 {
        /// @notice Inaugural.
        uint public constant version = 1;
        /// @notice Holds information about this collection.
        ContractMetadata.Data private metadata;
        /// @notice Holds the metadata for each token within this collection.
        mapping(uint => Token) private tokens;
        /// @notice The token metadata renderers registered with this collection.
        address[] public renderers;
        /// @notice The most recently minted token id.
        uint public latestTokenId;
        /// @notice Ethereum block height of when this collection was created.
        uint public initBlock;
        /// @notice Each mint is open for 24 hours.
        uint constant MINT_DURATION = 24 hours;
        /// @dev Emitted when a collector mints a token.
        event NewMint(uint indexed tokenId, uint unitPrice, uint amount, address minter);
        /// @dev Emitted when the artist registers a new Renderer contract.
        event NewRenderer(address indexed renderer, uint indexed index);
        /// @dev Emitted when the artist withdraws the contract balance.
        event Withdrawal(uint amount);
        /// @dev Thrown on the attempt to reinitialize the contract.
        error Initialized();
        /// @dev Thrown when trying to mint a piece after the mint window.
        error MintClosed();
        /// @dev Thrown when trying to mint a piece below its current price.
        error MintPriceNotMet();
        /// @dev Thrown when trying to mint a non existent token.
        error NonExistentToken();
        /// @dev Thrown when trying to change an existing token.
        error TokenAlreadyMinted();
        /// @dev Thrown when trying to create a token with a non existent renderer assigned.
        error NonExistentRenderer();
        /// @dev Thrown when the withdrawal fails.
        error WithdrawalFailed();
        /// @notice Initializes the collection contract.
        function init(
            string calldata contractName,
            string calldata contractSymbol,
            string calldata contractDescription,
            bytes[] calldata contractImage,
            address renderer,
            address owner
        ) external {
            if (initBlock > 0) revert Initialized();
            // Initialize with metadata.
            metadata.name        = contractName;
            metadata.symbol      = contractSymbol;
            metadata.description = contractDescription;
            // Write the contract image to storage.
            for (uint8 i = 0; i < contractImage.length; i++) {
                metadata.image.push(SSTORE2.write(contractImage[i]));
            }
            // Set the inial renderer
            renderers.push(renderer);
            // Setting the initialization block height prevents reinitialization
            initBlock = block.number;
            _transferOwnership(owner);
        }
        /// @notice Lets the artist create a new token.
        function create(
            string  calldata tokenName,
            string  calldata tokenDescription,
            bytes[] calldata tokenArtifact,
            uint32  tokenRenderer,
            uint128 tokenData
        ) public onlyOwner {
            if (renderers.length < tokenRenderer + 1) revert NonExistentRenderer();
            ++ latestTokenId;
            Token storage token = tokens[latestTokenId];
            token.name        = tokenName;
            token.description = tokenDescription;
            token.mintedBlock = uint32(block.number);
            token.closeAt     = uint64(block.timestamp + MINT_DURATION);
            token.renderer    = tokenRenderer;
            token.data        = tokenData;
            if (tokenArtifact.length > 0) {
                // Clear previously prepared artifact data.
                if (token.artifact.length > 0) {
                    delete token.artifact;
                }
                // Write the token artifact to storage.
                for (uint8 i = 0; i < tokenArtifact.length; i++) {
                    token.artifact.push(SSTORE2.write(tokenArtifact[i]));
                }
            }
            _mint(msg.sender, latestTokenId, 1, "");
        }
        /// @notice Lets the artist prepare artifacts that are too large to store in a single transaction.
        function prepareArtifact(uint tokenId, bytes[] calldata tokenArtifact, bool clear) external onlyOwner {
            if (tokenId <= latestTokenId) revert TokenAlreadyMinted();
            Token storage token = tokens[tokenId];
            if (token.artifact.length > 0 && clear) { delete token.artifact; }
            // Write the token artifact to storage.
            for (uint8 i = 0; i < tokenArtifact.length; i++) {
                token.artifact.push(SSTORE2.write(tokenArtifact[i]));
            }
        }
        /// @notice Get the bare token data for a given id.
        function get(uint tokenId) external view returns (
            string memory name,
            string memory description,
            address[] memory artifact,
            uint32 renderer,
            uint32 mintedBlock,
            uint64 closeAt,
            uint128 data
        ) {
            Token storage token = tokens[tokenId];
            return (
                token.name,
                token.description,
                token.artifact,
                token.renderer,
                token.mintedBlock,
                token.closeAt,
                token.data
            );
        }
        /// @notice Lets collectors purchase a token during its mint window.
        function mint(uint tokenId, uint amount) external payable {
            if (tokenId > latestTokenId) revert NonExistentToken();
            uint unitPrice = block.basefee * 60_000;
            uint mintPrice = unitPrice * amount;
            if (mintPrice > msg.value) revert MintPriceNotMet();
            if (mintOpenUntil(tokenId) < block.timestamp) revert MintClosed();
            _mint(msg.sender, tokenId, amount, "");
            emit NewMint(tokenId, unitPrice, amount, msg.sender);
        }
        /// @notice Check until when a mint is open.
        function mintOpenUntil(uint tokenId) public view returns (uint) {
            return tokens[tokenId].closeAt;
        }
        /// @notice Lets the artist register a new renderer to use for future mints.
        function registerRenderer(address renderer) external onlyOwner returns (uint) {
            renderers.push(renderer);
            uint index = renderers.length - 1;
            emit NewRenderer(renderer, index);
            return index;
        }
        /// @notice Lets the artist withdraw the contract balance.
        function withdraw() external onlyOwner {
            uint balance = address(this).balance;
            (bool success, ) = payable(owner()).call{value: balance}("");
            if (! success) revert WithdrawalFailed();
            emit Withdrawal(balance);
        }
        /// @notice Get the metadata for a given token id.
        function uri(uint tokenId) external override view returns (string memory) {
            if (tokenId > latestTokenId) revert NonExistentToken();
            Token memory token = tokens[tokenId];
            return IRenderer(renderers[token.renderer]).uri(tokenId, token);
        }
        /// @notice Get the metadata for this collection contract.
        function contractURI() public view returns (string memory) {
            return ContractMetadata.uri(metadata);
        }
        /// @notice Burn a given token & amount.
        function burn(address account, uint256 tokenId, uint256 amount) external {
            if (account != msg.sender && !isApprovedForAll(account, msg.sender)) {
                revert ERC1155MissingApprovalForAll(msg.sender, account);
            }
            _burn(account, tokenId, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    struct Token {
        string  name;            // token name
        string  description;    // token description
        address[] artifact;    // artifact pointers (image/artwork) data
        uint32  renderer;     // index of renderer contract address
        uint32  mintedBlock; // creation block height of the token
        uint64  closeAt;    // timestamp of mint completion
        uint128 data;      // optional data for renderers
    }
    

    File 2 of 2: Mint
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {Ownable} from "./Ownable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2Step is Ownable {
        address private _pendingOwner;
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            return _pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            _pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            delete _pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard ERC20 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
     */
    interface IERC20Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC20InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC20InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC20InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC20InvalidSpender(address spender);
    }
    /**
     * @dev Standard ERC721 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
     */
    interface IERC721Errors {
        /**
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
         * Used in balance queries.
         * @param owner Address of the current owner of a token.
         */
        error ERC721InvalidOwner(address owner);
        /**
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
         * @param tokenId Identifier number of a token.
         */
        error ERC721NonexistentToken(uint256 tokenId);
        /**
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param tokenId Identifier number of a token.
         * @param owner Address of the current owner of a token.
         */
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC721InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC721InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param tokenId Identifier number of a token.
         */
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC721InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC721InvalidOperator(address operator);
    }
    /**
     * @dev Standard ERC1155 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
     */
    interface IERC1155Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         * @param tokenId Identifier number of a token.
         */
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC1155InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC1155InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param owner Address of the current owner of a token.
         */
        error ERC1155MissingApprovalForAll(address operator, address owner);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC1155InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC1155InvalidOperator(address operator);
        /**
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
         * Used in batch transfers.
         * @param idsLength Length of the array of token identifiers
         * @param valuesLength Length of the array of token amounts
         */
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.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[EIP].
     */
    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.0.1) (token/ERC1155/IERC1155.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     */
    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`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        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 caller.
         */
        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.0.0) (token/ERC1155/IERC1155Receiver.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    /**
     * @dev Interface that must be implemented by smart contracts in order to receive
     * ERC-1155 token transfers.
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
    pragma solidity ^0.8.20;
    import {StorageSlot} from "./StorageSlot.sol";
    import {Math} from "./math/Math.sol";
    /**
     * @dev Collection of functions related to array types.
     */
    library Arrays {
        using StorageSlot for bytes32;
        /**
         * @dev Searches a sorted `array` and returns the first index that contains
         * a value greater or equal to `element`. If no such index exists (i.e. all
         * values in the array are strictly less than `element`), the array length is
         * returned. Time complexity O(log n).
         *
         * `array` is expected to be sorted in ascending order, and to contain no
         * repeated elements.
         */
        function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
            uint256 low = 0;
            uint256 high = array.length;
            if (high == 0) {
                return 0;
            }
            while (low < high) {
                uint256 mid = Math.average(low, high);
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
                // because Math.average rounds towards zero (it does integer division with truncation).
                if (unsafeAccess(array, mid).value > element) {
                    high = mid;
                } else {
                    low = mid + 1;
                }
            }
            // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
            if (low > 0 && unsafeAccess(array, low - 1).value == element) {
                return low - 1;
            } else {
                return low;
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getAddressSlot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getBytes32Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getUint256Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides a set of functions to operate with Base64 strings.
     */
    library Base64 {
        /**
         * @dev Base64 Encoding/Decoding Table
         */
        string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        /**
         * @dev Converts a `bytes` to its Bytes64 `string` representation.
         */
        function encode(bytes memory data) internal pure returns (string memory) {
            /**
             * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
             * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
             */
            if (data.length == 0) return "";
            // Loads the table into memory
            string memory table = _TABLE;
            // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
            // and split into 4 numbers of 6 bits.
            // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
            // - `data.length + 2`  -> Round up
            // - `/ 3`              -> Number of 3-bytes chunks
            // - `4 *`              -> 4 characters for each chunk
            string memory result = new string(4 * ((data.length + 2) / 3));
            /// @solidity memory-safe-assembly
            assembly {
                // Prepare the lookup table (skip the first "length" byte)
                let tablePtr := add(table, 1)
                // Prepare result pointer, jump over length
                let resultPtr := add(result, 0x20)
                let dataPtr := data
                let endPtr := add(data, mload(data))
                // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
                // set it to zero to make sure no dirty bytes are read in that section.
                let afterPtr := add(endPtr, 0x20)
                let afterCache := mload(afterPtr)
                mstore(afterPtr, 0x00)
                // Run over the input, 3 bytes at a time
                for {
                } lt(dataPtr, endPtr) {
                } {
                    // Advance 3 bytes
                    dataPtr := add(dataPtr, 3)
                    let input := mload(dataPtr)
                    // To write each character, shift the 3 byte (24 bits) chunk
                    // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                    // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                    // Use this as an index into the lookup table, mload an entire word
                    // so the desired character is in the least significant byte, and
                    // mstore8 this least significant byte into the result and continue.
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                    mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                    resultPtr := add(resultPtr, 1) // Advance
                }
                // Reset the value that was cached
                mstore(afterPtr, afterCache)
                // When data `bytes` is not exactly 3 bytes long
                // it is padded with `=` characters at the end
                switch mod(mload(data), 3)
                case 1 {
                    mstore8(sub(resultPtr, 1), 0x3d)
                    mstore8(sub(resultPtr, 2), 0x3d)
                }
                case 2 {
                    mstore8(sub(resultPtr, 1), 0x3d)
                }
            }
            return result;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
         * denominator == 0.
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
         * Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                uint256 twos = denominator & (0 - denominator);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                // works in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
         * towards zero.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard signed math utilities missing in the Solidity language.
     */
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```solidity
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(newImplementation.code.length > 0);
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
        struct BooleanSlot {
            bool value;
        }
        struct Bytes32Slot {
            bytes32 value;
        }
        struct Uint256Slot {
            uint256 value;
        }
        struct StringSlot {
            string value;
        }
        struct BytesSlot {
            bytes value;
        }
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` with member `value` located at `slot`.
         */
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
         */
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` with member `value` located at `slot`.
         */
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
         */
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
    pragma solidity ^0.8.20;
    import {Math} from "./math/Math.sol";
    import {SignedMath} from "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
        uint8 private constant ADDRESS_LENGTH = 20;
        /**
         * @dev The `value` string doesn't fit in the specified `length`.
         */
        error StringsInsufficientHexLength(uint256 value, uint256 length);
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toStringSigned(int256 value) internal pure returns (string memory) {
            return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            uint256 localValue = value;
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_DIGITS[localValue & 0xf];
                localValue >>= 4;
            }
            if (localValue != 0) {
                revert StringsInsufficientHexLength(value, length);
            }
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
         * representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
        }
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol";
    import { IERC1155              } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    import { IERC1155Receiver      } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
    import { IERC1155MetadataURI   } from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
    import { Context               } from "@openzeppelin/contracts/utils/Context.sol";
    import { IERC165, ERC165       } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    import { Arrays                } from "@openzeppelin/contracts/utils/Arrays.sol";
    import { IERC1155Errors        } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    /**
     * @dev Minimal implementation of the basic standard multi-token based on the OpenZeppelin contracts.
     * See https://eips.ethereum.org/EIPS/eip-1155
     */
    abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors, Ownable2Step {
        using Arrays for uint256[];
        using Arrays for address[];
        mapping(uint256 id => mapping(address account => uint256)) private _balances;
        mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
        constructor() Ownable(msg.sender) {}
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256 /* id */) external view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC1155-balanceOf}.
         */
        function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
            return _balances[id][account];
        }
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(
            address[] memory accounts,
            uint256[] memory ids
        ) public view virtual returns (uint256[] memory) {
            if (accounts.length != ids.length) {
                revert ERC1155InvalidArrayLength(ids.length, accounts.length);
            }
            uint256[] memory batchBalances = new uint256[](accounts.length);
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
            }
            return batchBalances;
        }
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
            return _operatorApprovals[account][operator];
        }
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
            address sender = _msgSender();
            if (from != sender && !isApprovedForAll(from, sender)) {
                revert ERC1155MissingApprovalForAll(sender, from);
            }
            _safeTransferFrom(from, to, id, value, data);
        }
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) public virtual {
            address sender = _msgSender();
            if (from != sender && !isApprovedForAll(from, sender)) {
                revert ERC1155MissingApprovalForAll(sender, from);
            }
            _safeBatchTransferFrom(from, to, ids, values, data);
        }
        /**
         * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
         * (or `to`) is the zero address.
         *
         * Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
         *   or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
         * - `ids` and `values` must have the same length.
         *
         * NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
         */
        function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
            if (ids.length != values.length) {
                revert ERC1155InvalidArrayLength(ids.length, values.length);
            }
            address operator = _msgSender();
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids.unsafeMemoryAccess(i);
                uint256 value = values.unsafeMemoryAccess(i);
                if (from != address(0)) {
                    uint256 fromBalance = _balances[id][from];
                    if (fromBalance < value) {
                        revert ERC1155InsufficientBalance(from, fromBalance, value, id);
                    }
                    unchecked {
                        // Overflow not possible: value <= fromBalance
                        _balances[id][from] = fromBalance - value;
                    }
                }
                if (to != address(0)) {
                    _balances[id][to] += value;
                }
            }
            if (ids.length == 1) {
                uint256 id = ids.unsafeMemoryAccess(0);
                uint256 value = values.unsafeMemoryAccess(0);
                emit TransferSingle(operator, from, to, id, value);
            } else {
                emit TransferBatch(operator, from, to, ids, values);
            }
        }
        /**
         * @dev Version of {_update} that performs the token acceptance check by calling
         * {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
         * contains code (eg. is a smart contract at the moment of execution).
         *
         * IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
         * update to the contract state after this function would break the check-effect-interaction pattern. Consider
         * overriding {_update} instead.
         */
        function _updateWithAcceptanceCheck(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) internal virtual {
            _update(from, to, ids, values);
            if (to != address(0)) {
                address operator = _msgSender();
                if (ids.length == 1) {
                    uint256 id = ids.unsafeMemoryAccess(0);
                    uint256 value = values.unsafeMemoryAccess(0);
                    _doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
                } else {
                    _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
                }
            }
        }
        /**
         * @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `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 memory data) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(from, to, ids, values, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         * - `ids` and `values` must have the same length.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            _updateWithAcceptanceCheck(from, to, ids, values, data);
        }
        /**
         * @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
            if (to == address(0)) {
                revert ERC1155InvalidReceiver(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(address(0), to, ids, values, data);
        }
        /**
         * @dev Destroys a `value` amount of tokens of type `id` from `from`
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `from` must have at least `value` amount of tokens of type `id`.
         */
        function _burn(address from, uint256 id, uint256 value) internal {
            if (from == address(0)) {
                revert ERC1155InvalidSender(address(0));
            }
            (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
            _updateWithAcceptanceCheck(from, address(0), ids, values, "");
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the zero address.
         */
        function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
            if (operator == address(0)) {
                revert ERC1155InvalidOperator(address(0));
            }
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
         * if it contains code at the moment of execution.
         */
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 value,
            bytes memory data
        ) private {
            if (to.code.length > 0) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        // Tokens rejected
                        revert ERC1155InvalidReceiver(to);
                    }
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        // non-ERC1155Receiver implementer
                        revert ERC1155InvalidReceiver(to);
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            }
        }
        /**
         * @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
         * if it contains code at the moment of execution.
         */
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory values,
            bytes memory data
        ) private {
            if (to.code.length > 0) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        // Tokens rejected
                        revert ERC1155InvalidReceiver(to);
                    }
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        // non-ERC1155Receiver implementer
                        revert ERC1155InvalidReceiver(to);
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            }
        }
        /**
         * @dev Creates an array in memory with only one value for each of the elements provided.
         */
        function _asSingletonArrays(
            uint256 element1,
            uint256 element2
        ) private pure returns (uint256[] memory array1, uint256[] memory array2) {
            /// @solidity memory-safe-assembly
            assembly {
                // Load the free memory pointer
                array1 := mload(0x40)
                // Set array length to 1
                mstore(array1, 1)
                // Store the single element at the next word after the length (where content starts)
                mstore(add(array1, 0x20), element1)
                // Repeat for next array locating it right after the first array
                array2 := add(array1, 0x40)
                mstore(array2, 1)
                mstore(add(array2, 0x20), element2)
                // Update the free memory pointer by pointing after the second array
                mstore(0x40, add(array2, 0x40))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Token } from "../types/Token.sol";
    interface IRenderer {
        function name () external pure returns (string memory);
        function version () external pure returns (uint version);
        function uri (uint tokenId, Token calldata token) external view returns (string memory);
        function imageURI (uint tokenId, Token calldata token) external view returns (string memory);
        function animationURI (uint tokenId, Token calldata token) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
    import { Base64  } from "@openzeppelin/contracts/utils/Base64.sol";
    import { SSTORE2 } from "./SSTORE2.sol";
    library ContractMetadata {
        struct Data {
            string name;
            string symbol;
            string description;
            address[] image;
        }
        function uri (Data memory data) external view returns (string memory) {
            bytes memory dataURI = abi.encodePacked(
                '{',
                    '"name": "', data.name, '",',
                    '"symbol": "', data.symbol, '",',
                    '"description": "', data.description, '",',
                    '"image": "', image(data), '"',
                '}'
            );
            return string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(dataURI)
                )
            );
        }
        function image (Data memory data) internal view returns (bytes memory content) {
            for (uint8 i = 0; i < data.image.length; i++) {
                content = abi.encodePacked(content, SSTORE2.read(data.image[i]));
            }
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Read and write to persistent storage at a fraction of the cost.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
    /// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
    library SSTORE2 {
        uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.
        /*//////////////////////////////////////////////////////////////
                                   WRITE LOGIC
        //////////////////////////////////////////////////////////////*/
        function write(bytes memory data) internal returns (address pointer) {
            // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
            bytes memory runtimeCode = abi.encodePacked(hex"00", data);
            bytes memory creationCode = abi.encodePacked(
                //---------------------------------------------------------------------------------------------------------------//
                // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
                //---------------------------------------------------------------------------------------------------------------//
                // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
                // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
                // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
                // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
                // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
                // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
                // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
                // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
                // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
                // 0xf3    |  0xf3               | RETURN       |                                                                //
                //---------------------------------------------------------------------------------------------------------------//
                hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
                runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
            );
            /// @solidity memory-safe-assembly
            assembly {
                // Deploy a new contract with the generated creation code.
                // We start 32 bytes into the code to avoid copying the byte length.
                pointer := create(0, add(creationCode, 32), mload(creationCode))
            }
            require(pointer != address(0), "DEPLOYMENT_FAILED");
        }
        /*//////////////////////////////////////////////////////////////
                                   READ LOGIC
        //////////////////////////////////////////////////////////////*/
        function read(address pointer) internal view returns (bytes memory) {
            return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
        }
        function read(address pointer, uint256 start) internal view returns (bytes memory) {
            start += DATA_OFFSET;
            return readBytecode(pointer, start, pointer.code.length - start);
        }
        function read(
            address pointer,
            uint256 start,
            uint256 end
        ) internal view returns (bytes memory) {
            start += DATA_OFFSET;
            end += DATA_OFFSET;
            require(pointer.code.length >= end, "OUT_OF_BOUNDS");
            return readBytecode(pointer, start, end - start);
        }
        /*//////////////////////////////////////////////////////////////
                              INTERNAL HELPER LOGIC
        //////////////////////////////////////////////////////////////*/
        function readBytecode(
            address pointer,
            uint256 start,
            uint256 size
        ) private view returns (bytes memory data) {
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                data := mload(0x40)
                // Update the free memory pointer to prevent overriding our data.
                // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
                // Adding 31 to size and running the result through the logic above ensures
                // the memory pointer remains word-aligned, following the Solidity convention.
                mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
                // Store the size of the data in the first 32 byte chunk of free memory.
                mstore(data, size)
                // Copy the code into memory right after the 32 bytes we used to store the size.
                extcodecopy(pointer, add(data, 32), start, size)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    import { ERC1155               } from "./ERC1155.sol";
    import { IRenderer             } from "./interfaces/IRenderer.sol";
    import { ContractMetadata      } from "./libraries/ContractMetadata.sol";
    import { SSTORE2               } from "./libraries/SSTORE2.sol";
    import { Token                 } from "./types/Token.sol";
    /// @notice To mint is a human right.
    /// @author Visualize Value
    contract Mint is ERC1155 {
        /// @notice Inaugural.
        uint public constant version = 1;
        /// @notice Holds information about this collection.
        ContractMetadata.Data private metadata;
        /// @notice Holds the metadata for each token within this collection.
        mapping(uint => Token) private tokens;
        /// @notice The token metadata renderers registered with this collection.
        address[] public renderers;
        /// @notice The most recently minted token id.
        uint public latestTokenId;
        /// @notice Ethereum block height of when this collection was created.
        uint public initBlock;
        /// @notice Each mint is open for 24 hours.
        uint constant MINT_DURATION = 24 hours;
        /// @dev Emitted when a collector mints a token.
        event NewMint(uint indexed tokenId, uint unitPrice, uint amount, address minter);
        /// @dev Emitted when the artist registers a new Renderer contract.
        event NewRenderer(address indexed renderer, uint indexed index);
        /// @dev Emitted when the artist withdraws the contract balance.
        event Withdrawal(uint amount);
        /// @dev Thrown on the attempt to reinitialize the contract.
        error Initialized();
        /// @dev Thrown when trying to mint a piece after the mint window.
        error MintClosed();
        /// @dev Thrown when trying to mint a piece below its current price.
        error MintPriceNotMet();
        /// @dev Thrown when trying to mint a non existent token.
        error NonExistentToken();
        /// @dev Thrown when trying to change an existing token.
        error TokenAlreadyMinted();
        /// @dev Thrown when trying to create a token with a non existent renderer assigned.
        error NonExistentRenderer();
        /// @dev Thrown when the withdrawal fails.
        error WithdrawalFailed();
        /// @notice Initializes the collection contract.
        function init(
            string calldata contractName,
            string calldata contractSymbol,
            string calldata contractDescription,
            bytes[] calldata contractImage,
            address renderer,
            address owner
        ) external {
            if (initBlock > 0) revert Initialized();
            // Initialize with metadata.
            metadata.name        = contractName;
            metadata.symbol      = contractSymbol;
            metadata.description = contractDescription;
            // Write the contract image to storage.
            for (uint8 i = 0; i < contractImage.length; i++) {
                metadata.image.push(SSTORE2.write(contractImage[i]));
            }
            // Set the inial renderer
            renderers.push(renderer);
            // Setting the initialization block height prevents reinitialization
            initBlock = block.number;
            _transferOwnership(owner);
        }
        /// @notice Lets the artist create a new token.
        function create(
            string  calldata tokenName,
            string  calldata tokenDescription,
            bytes[] calldata tokenArtifact,
            uint32  tokenRenderer,
            uint128 tokenData
        ) public onlyOwner {
            if (renderers.length < tokenRenderer + 1) revert NonExistentRenderer();
            ++ latestTokenId;
            Token storage token = tokens[latestTokenId];
            token.name        = tokenName;
            token.description = tokenDescription;
            token.mintedBlock = uint32(block.number);
            token.closeAt     = uint64(block.timestamp + MINT_DURATION);
            token.renderer    = tokenRenderer;
            token.data        = tokenData;
            if (tokenArtifact.length > 0) {
                // Clear previously prepared artifact data.
                if (token.artifact.length > 0) {
                    delete token.artifact;
                }
                // Write the token artifact to storage.
                for (uint8 i = 0; i < tokenArtifact.length; i++) {
                    token.artifact.push(SSTORE2.write(tokenArtifact[i]));
                }
            }
            _mint(msg.sender, latestTokenId, 1, "");
        }
        /// @notice Lets the artist prepare artifacts that are too large to store in a single transaction.
        function prepareArtifact(uint tokenId, bytes[] calldata tokenArtifact, bool clear) external onlyOwner {
            if (tokenId <= latestTokenId) revert TokenAlreadyMinted();
            Token storage token = tokens[tokenId];
            if (token.artifact.length > 0 && clear) { delete token.artifact; }
            // Write the token artifact to storage.
            for (uint8 i = 0; i < tokenArtifact.length; i++) {
                token.artifact.push(SSTORE2.write(tokenArtifact[i]));
            }
        }
        /// @notice Get the bare token data for a given id.
        function get(uint tokenId) external view returns (
            string memory name,
            string memory description,
            address[] memory artifact,
            uint32 renderer,
            uint32 mintedBlock,
            uint64 closeAt,
            uint128 data
        ) {
            Token storage token = tokens[tokenId];
            return (
                token.name,
                token.description,
                token.artifact,
                token.renderer,
                token.mintedBlock,
                token.closeAt,
                token.data
            );
        }
        /// @notice Lets collectors purchase a token during its mint window.
        function mint(uint tokenId, uint amount) external payable {
            if (tokenId > latestTokenId) revert NonExistentToken();
            uint unitPrice = block.basefee * 60_000;
            uint mintPrice = unitPrice * amount;
            if (mintPrice > msg.value) revert MintPriceNotMet();
            if (mintOpenUntil(tokenId) < block.timestamp) revert MintClosed();
            _mint(msg.sender, tokenId, amount, "");
            emit NewMint(tokenId, unitPrice, amount, msg.sender);
        }
        /// @notice Check until when a mint is open.
        function mintOpenUntil(uint tokenId) public view returns (uint) {
            return tokens[tokenId].closeAt;
        }
        /// @notice Lets the artist register a new renderer to use for future mints.
        function registerRenderer(address renderer) external onlyOwner returns (uint) {
            renderers.push(renderer);
            uint index = renderers.length - 1;
            emit NewRenderer(renderer, index);
            return index;
        }
        /// @notice Lets the artist withdraw the contract balance.
        function withdraw() external onlyOwner {
            uint balance = address(this).balance;
            (bool success, ) = payable(owner()).call{value: balance}("");
            if (! success) revert WithdrawalFailed();
            emit Withdrawal(balance);
        }
        /// @notice Get the metadata for a given token id.
        function uri(uint tokenId) external override view returns (string memory) {
            if (tokenId > latestTokenId) revert NonExistentToken();
            Token memory token = tokens[tokenId];
            return IRenderer(renderers[token.renderer]).uri(tokenId, token);
        }
        /// @notice Get the metadata for this collection contract.
        function contractURI() public view returns (string memory) {
            return ContractMetadata.uri(metadata);
        }
        /// @notice Burn a given token & amount.
        function burn(address account, uint256 tokenId, uint256 amount) external {
            if (account != msg.sender && !isApprovedForAll(account, msg.sender)) {
                revert ERC1155MissingApprovalForAll(msg.sender, account);
            }
            _burn(account, tokenId, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;
    struct Token {
        string  name;            // token name
        string  description;    // token description
        address[] artifact;    // artifact pointers (image/artwork) data
        uint32  renderer;     // index of renderer contract address
        uint32  mintedBlock; // creation block height of the token
        uint64  closeAt;    // timestamp of mint completion
        uint128 data;      // optional data for renderers
    }