ETH Price: $3,008.39 (+1.06%)

Transaction Decoder

Block:
18722951 at Dec-05-2023 09:53:47 PM +UTC
Transaction Fee:
0.00808464310923597 ETH $24.32
Gas Used:
148,110 Gas / 54.585396727 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x86f2aD57...68b63cA17
(CryptoDoodez: Deployer)
0.056436161490080474 Eth
Nonce: 3287
0.048351518380844504 Eth
Nonce: 3288
0.00808464310923597
(Fee Recipient: 0xe68...127)
291.324939348736309599 Eth291.324954159736309599 Eth0.000014811

Execution Trace

TWCloneFactory.deployProxyByImplementation( _implementation=0x7aF3cC1fDF64D9bF5b3079544Cc096FD5daD43C9, _data=0x92928E0400000000000000000000000086F2AD57B59BB5BE8091A0A5FDBECB168B63CA170000000000000000000000007B4B550D6CBF55441F6153C71A5D173D860D83FE9975837B5C57FA933700B6FD9CD1AF739BB9463E264930C0A1C39927FC8A4ADA00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000037697066733A2F2F516D516744656D503232535235566A566E6454484D6E646D6866396A424B7344756D6F48687036523765625968392F30000000000000000000, _salt=3138373232393437000000000000000000000000000000000000000000000000 ) => ( deployedProxy=0x0000000000000000000000000000000000000000 )
  • 0x6a8f037b721b065f5dc3fa95989bbdb1bad552a4.3d602d80( )
  • 0x6a8f037b721b065f5dc3fa95989bbdb1bad552a4.92928e04( )
    • DropERC1155M.initialize( _defaultAdmin=0x86f2aD57b59bb5BE8091A0a5fDBecb168b63cA17, __originalContract=0x7b4B550d6cbf55441f6153c71A5D173d860d83fE, _ownershipMerkleRoot=9975837B5C57FA933700B6FD9CD1AF739BB9463E264930C0A1C39927FC8A4ADA, _contractURI=ipfs://QmQgDemP22SR5VjVndTHMndmhf9jBKsDumoHhp6R7ebYh9/0 )
      • DropERC1155.STATICCALL( )
        • DropERC1155.DELEGATECALL( )
        • DropERC1155.STATICCALL( )
          • DropERC1155.DELEGATECALL( )
            File 1 of 4: TWCloneFactory
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.11;
            /// @author thirdweb
            //   $$\\     $$\\       $$\\                 $$\\                         $$\\
            //   $$ |    $$ |      \\__|                $$ |                        $$ |
            // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
            // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
            //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
            //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
            //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
            //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
            import "./extension/interface/IContractFactory.sol";
            import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
            import "@openzeppelin/contracts/utils/Multicall.sol";
            import "@openzeppelin/contracts/proxy/Clones.sol";
            contract TWCloneFactory is Multicall, ERC2771Context, IContractFactory {
                /// @dev Emitted when a proxy is deployed.
                event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer);
                constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {}
                /// @dev Deploys a proxy that points to the given implementation.
                function deployProxyByImplementation(
                    address _implementation,
                    bytes memory _data,
                    bytes32 _salt
                ) public override returns (address deployedProxy) {
                    bytes32 salthash = keccak256(abi.encodePacked(_msgSender(), _salt));
                    deployedProxy = Clones.cloneDeterministic(_implementation, salthash);
                    emit ProxyDeployed(_implementation, deployedProxy, _msgSender());
                    if (_data.length > 0) {
                        // slither-disable-next-line unused-return
                        Address.functionCall(deployedProxy, _data);
                    }
                }
                function _msgSender() internal view virtual override returns (address sender) {
                    return ERC2771Context._msgSender();
                }
                function _msgData() internal view virtual override returns (bytes calldata) {
                    return ERC2771Context._msgData();
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            interface IContractFactory {
                /**
                 *  @notice Deploys a proxy that points to that points to the given implementation.
                 *
                 *  @param implementation           Address of the implementation to point to.
                 *
                 *  @param data                     Additional data to pass to the proxy constructor or any other data useful during deployement.
                 *  @param salt                     Salt to use for the deterministic address generation.
                 */
                function deployProxyByImplementation(
                    address implementation,
                    bytes memory data,
                    bytes32 salt
                ) external returns (address);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
            pragma solidity ^0.8.9;
            import "../utils/Context.sol";
            /**
             * @dev Context variant with ERC2771 support.
             */
            abstract contract ERC2771Context is Context {
                /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
                address private immutable _trustedForwarder;
                /// @custom:oz-upgrades-unsafe-allow constructor
                constructor(address trustedForwarder) {
                    _trustedForwarder = trustedForwarder;
                }
                function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                    return forwarder == _trustedForwarder;
                }
                function _msgSender() internal view virtual override returns (address sender) {
                    if (isTrustedForwarder(msg.sender)) {
                        // The assembly code is more direct than the Solidity version using `abi.decode`.
                        /// @solidity memory-safe-assembly
                        assembly {
                            sender := shr(96, calldataload(sub(calldatasize(), 20)))
                        }
                    } else {
                        return super._msgSender();
                    }
                }
                function _msgData() internal view virtual override returns (bytes calldata) {
                    if (isTrustedForwarder(msg.sender)) {
                        return msg.data[:msg.data.length - 20];
                    } else {
                        return super._msgData();
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
             * deploying minimal proxy contracts, also known as "clones".
             *
             * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
             * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
             *
             * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
             * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
             * deterministic method.
             *
             * _Available since v3.4._
             */
            library Clones {
                /**
                 * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
                 *
                 * This function uses the create opcode, which should never revert.
                 */
                function clone(address implementation) internal returns (address instance) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ptr := mload(0x40)
                        mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                        mstore(add(ptr, 0x14), shl(0x60, implementation))
                        mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                        instance := create(0, ptr, 0x37)
                    }
                    require(instance != address(0), "ERC1167: create failed");
                }
                /**
                 * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
                 *
                 * This function uses the create2 opcode and a `salt` to deterministically deploy
                 * the clone. Using the same `implementation` and `salt` multiple time will revert, since
                 * the clones cannot be deployed twice at the same address.
                 */
                function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ptr := mload(0x40)
                        mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                        mstore(add(ptr, 0x14), shl(0x60, implementation))
                        mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                        instance := create2(0, ptr, 0x37, salt)
                    }
                    require(instance != address(0), "ERC1167: create2 failed");
                }
                /**
                 * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
                 */
                function predictDeterministicAddress(
                    address implementation,
                    bytes32 salt,
                    address deployer
                ) internal pure returns (address predicted) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let ptr := mload(0x40)
                        mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                        mstore(add(ptr, 0x14), shl(0x60, implementation))
                        mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
                        mstore(add(ptr, 0x38), shl(0x60, deployer))
                        mstore(add(ptr, 0x4c), salt)
                        mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                        predicted := keccak256(add(ptr, 0x37), 0x55)
                    }
                }
                /**
                 * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
                 */
                function predictDeterministicAddress(address implementation, bytes32 salt)
                    internal
                    view
                    returns (address predicted)
                {
                    return predictDeterministicAddress(implementation, salt, address(this));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract Context {
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
            pragma solidity ^0.8.0;
            import "./Address.sol";
            /**
             * @dev Provides a function to batch together multiple calls in a single external call.
             *
             * _Available since v4.1._
             */
            abstract contract Multicall {
                /**
                 * @dev Receives and executes a batch of function calls on this contract.
                 */
                function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                    results = new bytes[](data.length);
                    for (uint256 i = 0; i < data.length; i++) {
                        results[i] = Address.functionDelegateCall(address(this), data[i]);
                    }
                    return results;
                }
            }
            

            File 2 of 4: DropERC1155M
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
                @title ERC-1155 Multi Token Standard
                @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
                Note: The ERC-165 identifier for this interface is 0xd9b67a26.
             */
            interface IERC1155 {
                /**
                    @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
                    The `_operator` argument MUST be msg.sender.
                    The `_from` argument MUST be the address of the holder whose balance is decreased.
                    The `_to` argument MUST be the address of the recipient whose balance is increased.
                    The `_id` argument MUST be the token type being transferred.
                    The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
                    When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
                    When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
                */
                event TransferSingle(
                    address indexed _operator,
                    address indexed _from,
                    address indexed _to,
                    uint256 _id,
                    uint256 _value
                );
                /**
                    @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
                    The `_operator` argument MUST be msg.sender.
                    The `_from` argument MUST be the address of the holder whose balance is decreased.
                    The `_to` argument MUST be the address of the recipient whose balance is increased.
                    The `_ids` argument MUST be the list of tokens being transferred.
                    The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
                    When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
                    When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
                */
                event TransferBatch(
                    address indexed _operator,
                    address indexed _from,
                    address indexed _to,
                    uint256[] _ids,
                    uint256[] _values
                );
                /**
                    @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absense of an event assumes disabled).
                */
                event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
                /**
                    @dev MUST emit when the URI is updated for a token ID.
                    URIs are defined in RFC 3986.
                    The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
                */
                event URI(string _value, uint256 indexed _id);
                /**
                    @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
                    @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
                    MUST revert if `_to` is the zero address.
                    MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
                    MUST revert on any other error.
                    MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
                    After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                    @param _from    Source address
                    @param _to      Target address
                    @param _id      ID of the token type
                    @param _value   Transfer amount
                    @param _data    Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
                */
                function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
                /**
                    @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
                    @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
                    MUST revert if `_to` is the zero address.
                    MUST revert if length of `_ids` is not the same as length of `_values`.
                    MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
                    MUST revert on any other error.
                    MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
                    Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
                    After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
                    @param _from    Source address
                    @param _to      Target address
                    @param _ids     IDs of each token type (order and length must match _values array)
                    @param _values  Transfer amounts per token type (order and length must match _ids array)
                    @param _data    Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
                */
                function safeBatchTransferFrom(
                    address _from,
                    address _to,
                    uint256[] calldata _ids,
                    uint256[] calldata _values,
                    bytes calldata _data
                ) external;
                /**
                    @notice Get the balance of an account's Tokens.
                    @param _owner  The address of the token holder
                    @param _id     ID of the Token
                    @return        The _owner's balance of the Token type requested
                 */
                function balanceOf(address _owner, uint256 _id) external view returns (uint256);
                /**
                    @notice Get the balance of multiple account/token pairs
                    @param _owners The addresses of the token holders
                    @param _ids    ID of the Tokens
                    @return        The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair)
                 */
                function balanceOfBatch(
                    address[] calldata _owners,
                    uint256[] calldata _ids
                ) external view returns (uint256[] memory);
                /**
                    @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
                    @dev MUST emit the ApprovalForAll event on success.
                    @param _operator  Address to add to the set of authorized operators
                    @param _approved  True if the operator is approved, false to revoke approval
                */
                function setApprovalForAll(address _operator, bool _approved) external;
                /**
                    @notice Queries the approval status of an operator for a given owner.
                    @param _owner     The owner of the Tokens
                    @param _operator  Address of authorized operator
                    @return           True if the operator is approved, false if not
                */
                function isApprovedForAll(address _owner, address _operator) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * [EIP](https://eips.ethereum.org/EIPS/eip-165).
             *
             * 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
                 * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             * @title ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            interface IERC20 {
                function totalSupply() external view returns (uint256);
                function balanceOf(address who) external view returns (uint256);
                function allowance(address owner, address spender) external view returns (uint256);
                function transfer(address to, uint256 value) external returns (bool);
                function approve(address spender, uint256 value) external returns (bool);
                function transferFrom(address from, address to, uint256 value) external returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
                event Approval(address indexed owner, address indexed spender, uint256 value);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981 is IERC165 {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
                 */
                function royaltyInfo(
                    uint256 tokenId,
                    uint256 salePrice
                ) external view returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  @title   Batch-mint Metadata
             *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
             *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
             *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
             */
            contract BatchMintMetadata {
                /// @dev Invalid index for batch
                error BatchMintInvalidBatchId(uint256 index);
                /// @dev Invalid token
                error BatchMintInvalidTokenId(uint256 tokenId);
                /// @dev Metadata frozen
                error BatchMintMetadataFrozen(uint256 batchId);
                /// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99}
                uint256[] private batchIds;
                /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                mapping(uint256 => string) private baseURI;
                /// @dev Mapping from id of a batch of tokens => to whether the base URI for the respective batch of tokens is frozen.
                mapping(uint256 => bool) public batchFrozen;
                /// @dev This event emits when the metadata of all tokens are frozen.
                /// While not currently supported by marketplaces, this event allows
                /// future indexing if desired.
                event MetadataFrozen();
                // @dev This event emits when the metadata of a range of tokens is updated.
                /// So that the third-party platforms such as NFT market could
                /// timely update the images and related attributes of the NFTs.
                event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
                /**
                 *  @notice         Returns the count of batches of NFTs.
                 *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                 *                  See {batchIds}.
                 */
                function getBaseURICount() public view returns (uint256) {
                    return batchIds.length;
                }
                /**
                 *  @notice         Returns the ID for the batch of tokens at the given index.
                 *  @dev            See {getBaseURICount}.
                 *  @param _index   Index of the desired batch in batchIds array.
                 */
                function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                    if (_index >= getBaseURICount()) {
                        revert BatchMintInvalidBatchId(_index);
                    }
                    return batchIds[_index];
                }
                /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            index = i;
                            batchId = indices[i];
                            return (batchId, index);
                        }
                    }
                    revert BatchMintInvalidTokenId(_tokenId);
                }
                /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            return baseURI[indices[i]];
                        }
                    }
                    revert BatchMintInvalidTokenId(_tokenId);
                }
                /// @dev returns the starting tokenId of a given batchId.
                function _getBatchStartId(uint256 _batchID) internal view returns (uint256) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i++) {
                        if (_batchID == indices[i]) {
                            if (i > 0) {
                                return indices[i - 1];
                            }
                            return 0;
                        }
                    }
                    revert BatchMintInvalidBatchId(_batchID);
                }
                /// @dev Sets the base URI for the batch of tokens with the given batchId.
                function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                    if (batchFrozen[_batchId]) {
                        revert BatchMintMetadataFrozen(_batchId);
                    }
                    baseURI[_batchId] = _baseURI;
                    emit BatchMetadataUpdate(_getBatchStartId(_batchId), _batchId);
                }
                /// @dev Freezes the base URI for the batch of tokens with the given batchId.
                function _freezeBaseURI(uint256 _batchId) internal {
                    string memory baseURIForBatch = baseURI[_batchId];
                    if (bytes(baseURIForBatch).length == 0) {
                        revert BatchMintInvalidBatchId(_batchId);
                    }
                    batchFrozen[_batchId] = true;
                    emit MetadataFrozen();
                }
                /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                function _batchMintMetadata(
                    uint256 _startId,
                    uint256 _amountToMint,
                    string memory _baseURIForTokens
                ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                    batchId = _startId + _amountToMint;
                    nextTokenIdToMint = batchId;
                    batchIds.push(batchId);
                    baseURI[batchId] = _baseURIForTokens;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IContractMetadata.sol";
            /**
             *  @title   Contract Metadata
             *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *           for you contract.
             *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            abstract contract ContractMetadata is IContractMetadata {
                /// @dev The sender is not authorized to perform the action
                error ContractMetadataUnauthorized();
                /// @notice Returns the contract metadata URI.
                string public override contractURI;
                /**
                 *  @notice         Lets a contract admin set the URI for contract-level metadata.
                 *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                 *                  See {_canSetContractURI}.
                 *                  Emits {ContractURIUpdated Event}.
                 *
                 *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function setContractURI(string memory _uri) external override {
                    if (!_canSetContractURI()) {
                        revert ContractMetadataUnauthorized();
                    }
                    _setupContractURI(_uri);
                }
                /// @dev Lets a contract admin set the URI for contract-level metadata.
                function _setupContractURI(string memory _uri) internal {
                    string memory prevURI = contractURI;
                    contractURI = _uri;
                    emit ContractURIUpdated(prevURI, _uri);
                }
                /// @dev Returns whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IDrop1155.sol";
            import "../lib/MerkleProof.sol";
            abstract contract Drop1155 is IDrop1155 {
                /// @dev The sender is not authorized to perform the action
                error DropUnauthorized();
                /// @dev Exceeded the max token total supply
                error DropExceedMaxSupply();
                /// @dev No active claim condition
                error DropNoActiveCondition();
                /// @dev Claim condition invalid currency or price
                error DropClaimInvalidTokenPrice(
                    address expectedCurrency,
                    uint256 expectedPricePerToken,
                    address actualCurrency,
                    uint256 actualExpectedPricePerToken
                );
                /// @dev Claim condition exceeded limit
                error DropClaimExceedLimit(uint256 expected, uint256 actual);
                /// @dev Claim condition exceeded max supply
                error DropClaimExceedMaxSupply(uint256 expected, uint256 actual);
                /// @dev Claim condition not started yet
                error DropClaimNotStarted(uint256 expected, uint256 actual);
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => the set of all claim conditions, at any given moment, for tokens of the token ID.
                mapping(uint256 => ClaimConditionList) public claimCondition;
                /*///////////////////////////////////////////////////////////////
                                        Drop logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets an account claim tokens.
                function claim(
                    address _receiver,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) public payable virtual override {
                    _beforeClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                    uint256 activeConditionId = getActiveClaimConditionId(_tokenId);
                    verifyClaim(
                        activeConditionId,
                        _dropMsgSender(),
                        _tokenId,
                        _quantity,
                        _currency,
                        _pricePerToken,
                        _allowlistProof
                    );
                    // Update contract state.
                    claimCondition[_tokenId].conditions[activeConditionId].supplyClaimed += _quantity;
                    claimCondition[_tokenId].supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                    // If there's a price, collect price.
                    collectPriceOnClaim(_tokenId, address(0), _quantity, _currency, _pricePerToken);
                    // Mint the relevant NFTs to claimer.
                    transferTokensOnClaim(_receiver, _tokenId, _quantity);
                    emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, _tokenId, _quantity);
                    _afterClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                }
                /// @dev Lets a contract admin set claim conditions.
                function setClaimConditions(
                    uint256 _tokenId,
                    ClaimCondition[] calldata _conditions,
                    bool _resetClaimEligibility
                ) external virtual override {
                    if (!_canSetClaimConditions()) {
                        revert DropUnauthorized();
                    }
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    uint256 existingStartIndex = conditionList.currentStartId;
                    uint256 existingPhaseCount = conditionList.count;
                    /**
                     *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                     *  by `supplyClaimedByWallet`.
                     */
                    uint256 newStartIndex = existingStartIndex;
                    if (_resetClaimEligibility) {
                        newStartIndex = existingStartIndex + existingPhaseCount;
                    }
                    conditionList.count = _conditions.length;
                    conditionList.currentStartId = newStartIndex;
                    uint256 lastConditionStartTimestamp;
                    for (uint256 i = 0; i < _conditions.length; i++) {
                        require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                        uint256 supplyClaimedAlready = conditionList.conditions[newStartIndex + i].supplyClaimed;
                        if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                            revert DropExceedMaxSupply();
                        }
                        conditionList.conditions[newStartIndex + i] = _conditions[i];
                        conditionList.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                        lastConditionStartTimestamp = _conditions[i].startTimestamp;
                    }
                    /**
                     *  Gas refunds (as much as possible)
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                     *
                     *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                     *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                     *  by the conditions in `_conditions`.
                     */
                    if (_resetClaimEligibility) {
                        for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                            delete conditionList.conditions[i];
                        }
                    } else {
                        if (existingPhaseCount > _conditions.length) {
                            for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                delete conditionList.conditions[newStartIndex + i];
                            }
                        }
                    }
                    emit ClaimConditionsUpdated(_tokenId, _conditions, _resetClaimEligibility);
                }
                /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                function verifyClaim(
                    uint256 _conditionId,
                    address _claimer,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof
                ) public view virtual returns (bool isOverride) {
                    ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].conditions[_conditionId];
                    uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                    uint256 claimPrice = currentClaimPhase.pricePerToken;
                    address claimCurrency = currentClaimPhase.currency;
                    /*
                     * Here `isOverride` implies that if the merkle proof verification fails,
                     * the claimer would claim through open claim limit instead of allowlisted limit.
                     */
                    if (currentClaimPhase.merkleRoot != bytes32(0)) {
                        (isOverride, ) = MerkleProof.verify(
                            _allowlistProof.proof,
                            currentClaimPhase.merkleRoot,
                            keccak256(
                                abi.encodePacked(
                                    _claimer,
                                    _allowlistProof.quantityLimitPerWallet,
                                    _allowlistProof.pricePerToken,
                                    _allowlistProof.currency
                                )
                            )
                        );
                    }
                    if (isOverride) {
                        claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                            ? _allowlistProof.quantityLimitPerWallet
                            : claimLimit;
                        claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                            ? _allowlistProof.pricePerToken
                            : claimPrice;
                        claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                            ? _allowlistProof.currency
                            : claimCurrency;
                    }
                    uint256 supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                    if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                        revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice);
                    }
                    if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                        revert DropClaimExceedLimit(claimLimit, _quantity + supplyClaimedByWallet);
                    }
                    if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                        revert DropClaimExceedMaxSupply(
                            currentClaimPhase.maxClaimableSupply,
                            currentClaimPhase.supplyClaimed + _quantity
                        );
                    }
                    if (currentClaimPhase.startTimestamp > block.timestamp) {
                        revert DropClaimNotStarted(currentClaimPhase.startTimestamp, block.timestamp);
                    }
                }
                /// @dev At any given moment, returns the uid for the active claim condition.
                function getActiveClaimConditionId(uint256 _tokenId) public view returns (uint256) {
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    for (uint256 i = conditionList.currentStartId + conditionList.count; i > conditionList.currentStartId; i--) {
                        if (block.timestamp >= conditionList.conditions[i - 1].startTimestamp) {
                            return i - 1;
                        }
                    }
                    revert DropNoActiveCondition();
                }
                /// @dev Returns the claim condition at the given uid.
                function getClaimConditionById(
                    uint256 _tokenId,
                    uint256 _conditionId
                ) external view returns (ClaimCondition memory condition) {
                    condition = claimCondition[_tokenId].conditions[_conditionId];
                }
                /// @dev Returns the supply claimed by claimer for a given conditionId.
                function getSupplyClaimedByWallet(
                    uint256 _tokenId,
                    uint256 _conditionId,
                    address _claimer
                ) public view returns (uint256 supplyClaimedByWallet) {
                    supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                }
                /*////////////////////////////////////////////////////////////////////
                    Optional hooks that can be implemented in the derived contract
                ///////////////////////////////////////////////////////////////////*/
                /// @dev Exposes the ability to override the msg sender.
                function _dropMsgSender() internal virtual returns (address) {
                    return msg.sender;
                }
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /// @dev Runs after every `claim` function call.
                function _afterClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /*///////////////////////////////////////////////////////////////
                    Virtual functions: to be implemented in derived contract
                //////////////////////////////////////////////////////////////*/
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256 _tokenId,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal virtual;
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal virtual;
                /// @dev Determine what wallet can update claim conditions
                function _canSetClaimConditions() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/ILazyMint.sol";
            import "./BatchMintMetadata.sol";
            /**
             *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                /// @dev The sender is not authorized to perform the action
                error LazyMintUnauthorized();
                error LazyMintInvalidAmount();
                /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                uint256 internal nextTokenIdToLazyMint;
                /**
                 *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                 *
                 *  @param _amount           The number of NFTs to lazy mint.
                 *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                 *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 _amount,
                    string calldata _baseURIForTokens,
                    bytes calldata _data
                ) public virtual override returns (uint256 batchId) {
                    if (!_canLazyMint()) {
                        revert LazyMintUnauthorized();
                    }
                    if (_amount == 0) {
                        revert LazyMintInvalidAmount();
                    }
                    uint256 startId = nextTokenIdToLazyMint;
                    (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                    emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                    return batchId;
                }
                /// @dev Returns whether lazy minting can be performed in the given execution context.
                function _canLazyMint() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IOwnable.sol";
            /**
             *  @title   Ownable
             *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *           information about who the contract's owner is.
             */
            abstract contract Ownable is IOwnable {
                /// @dev The sender is not authorized to perform the action
                error OwnableUnauthorized();
                /// @dev Owner of the contract (purpose: OpenSea compatibility)
                address private _owner;
                /// @dev Reverts if caller is not the owner.
                modifier onlyOwner() {
                    if (msg.sender != _owner) {
                        revert OwnableUnauthorized();
                    }
                    _;
                }
                /**
                 *  @notice Returns the owner of the contract.
                 */
                function owner() public view override returns (address) {
                    return _owner;
                }
                /**
                 *  @notice Lets an authorized wallet set a new owner for the contract.
                 *  @param _newOwner The address to set as the new owner of the contract.
                 */
                function setOwner(address _newOwner) external override {
                    if (!_canSetOwner()) {
                        revert OwnableUnauthorized();
                    }
                    _setupOwner(_newOwner);
                }
                /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                function _setupOwner(address _newOwner) internal {
                    address _prevOwner = _owner;
                    _owner = _newOwner;
                    emit OwnerUpdated(_prevOwner, _newOwner);
                }
                /// @dev Returns whether owner can be set in the given execution context.
                function _canSetOwner() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IPermissions.sol";
            import "../lib/Strings.sol";
            /**
             *  @title   Permissions
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
             */
            contract Permissions is IPermissions {
                /// @dev The `account` is missing a role.
                error PermissionsUnauthorizedAccount(address account, bytes32 neededRole);
                /// @dev The `account` already is a holder of `role`
                error PermissionsAlreadyGranted(address account, bytes32 role);
                /// @dev Invalid priviledge to revoke
                error PermissionsInvalidPermission(address expected, address actual);
                /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                mapping(bytes32 => mapping(address => bool)) private _hasRole;
                /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                mapping(bytes32 => bytes32) private _getRoleAdmin;
                /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                modifier onlyRole(bytes32 role) {
                    _checkRole(role, msg.sender);
                    _;
                }
                /**
                 *  @notice         Checks whether an account has a particular role.
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRole(bytes32 role, address account) public view override returns (bool) {
                    return _hasRole[role][account];
                }
                /**
                 *  @notice         Checks whether an account has a particular role;
                 *                  role restrictions can be swtiched on and off.
                 *
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *                  Role restrictions can be swtiched on and off:
                 *                      - If address(0) has ROLE, then the ROLE restrictions
                 *                        don't apply.
                 *                      - If address(0) does not have ROLE, then the ROLE
                 *                        restrictions will apply.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                    if (!_hasRole[role][address(0)]) {
                        return _hasRole[role][account];
                    }
                    return true;
                }
                /**
                 *  @notice         Returns the admin role that controls the specified role.
                 *  @dev            See {grantRole} and {revokeRole}.
                 *                  To change a role's admin, use {_setRoleAdmin}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                    return _getRoleAdmin[role];
                }
                /**
                 *  @notice         Grants a role to an account, if not previously granted.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleGranted Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account to which the role is being granted.
                 */
                function grantRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    if (_hasRole[role][account]) {
                        revert PermissionsAlreadyGranted(account, role);
                    }
                    _setupRole(role, account);
                }
                /**
                 *  @notice         Revokes role from an account.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function revokeRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    _revokeRole(role, account);
                }
                /**
                 *  @notice         Revokes role from the account.
                 *  @dev            Caller must have the `role`, with caller being the same as `account`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function renounceRole(bytes32 role, address account) public virtual override {
                    if (msg.sender != account) {
                        revert PermissionsInvalidPermission(msg.sender, account);
                    }
                    _revokeRole(role, account);
                }
                /// @dev Sets `adminRole` as `role`'s admin role.
                function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                    bytes32 previousAdminRole = _getRoleAdmin[role];
                    _getRoleAdmin[role] = adminRole;
                    emit RoleAdminChanged(role, previousAdminRole, adminRole);
                }
                /// @dev Sets up `role` for `account`
                function _setupRole(bytes32 role, address account) internal virtual {
                    _hasRole[role][account] = true;
                    emit RoleGranted(role, account, msg.sender);
                }
                /// @dev Revokes `role` from `account`
                function _revokeRole(bytes32 role, address account) internal virtual {
                    _checkRole(role, account);
                    delete _hasRole[role][account];
                    emit RoleRevoked(role, account, msg.sender);
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRole(bytes32 role, address account) internal view virtual {
                    if (!_hasRole[role][account]) {
                        revert PermissionsUnauthorizedAccount(account, role);
                    }
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                    if (!hasRoleWithSwitch(role, account)) {
                        revert PermissionsUnauthorizedAccount(account, role);
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IPermissionsEnumerable.sol";
            import "./Permissions.sol";
            /**
             *  @title   PermissionsEnumerable
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
             *           Also provides interfaces to view all members with a given role, and total count of members.
             */
            contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                /**
                 *  @notice A data structure to store data of members for a given role.
                 *
                 *  @param index    Current index in the list of accounts that have a role.
                 *  @param members  map from index => address of account that has a role
                 *  @param indexOf  map from address => index which the account has.
                 */
                struct RoleMembers {
                    uint256 index;
                    mapping(uint256 => address) members;
                    mapping(address => uint256) indexOf;
                }
                /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                mapping(bytes32 => RoleMembers) private roleMembers;
                /**
                 *  @notice         Returns the role-member from a list of members for a role,
                 *                  at a given index.
                 *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param index    Index in list of current members for the role.
                 *
                 *  @return member  Address of account that has `role`
                 */
                function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                    uint256 currentIndex = roleMembers[role].index;
                    uint256 check;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            if (check == index) {
                                member = roleMembers[role].members[i];
                                return member;
                            }
                            check += 1;
                        } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                            check += 1;
                        }
                    }
                }
                /**
                 *  @notice         Returns total number of accounts that have a role.
                 *  @dev            Returns `count` of accounts that have `role`.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *
                 *  @return count   Total number of accounts that have `role`
                 */
                function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                    uint256 currentIndex = roleMembers[role].index;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            count += 1;
                        }
                    }
                    if (hasRole(role, address(0))) {
                        count += 1;
                    }
                }
                /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                ///      See {_removeMember}
                function _revokeRole(bytes32 role, address account) internal override {
                    super._revokeRole(role, account);
                    _removeMember(role, account);
                }
                /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                ///      See {_addMember}
                function _setupRole(bytes32 role, address account) internal override {
                    super._setupRole(role, account);
                    _addMember(role, account);
                }
                /// @dev adds `account` to {roleMembers}, for `role`
                function _addMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].index;
                    roleMembers[role].index += 1;
                    roleMembers[role].members[idx] = account;
                    roleMembers[role].indexOf[account] = idx;
                }
                /// @dev removes `account` from {roleMembers}, for `role`
                function _removeMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].indexOf[account];
                    delete roleMembers[role].members[idx];
                    delete roleMembers[role].indexOf[account];
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IPrimarySale.sol";
            /**
             *  @title   Primary Sale
             *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *           primary sales, if desired.
             */
            abstract contract PrimarySale is IPrimarySale {
                /// @dev The sender is not authorized to perform the action
                error PrimarySaleUnauthorized();
                /// @dev The recipient is invalid
                error PrimarySaleInvalidRecipient(address recipient);
                /// @dev The address that receives all primary sales value.
                address private recipient;
                /// @dev Returns primary sale recipient address.
                function primarySaleRecipient() public view override returns (address) {
                    return recipient;
                }
                /**
                 *  @notice         Updates primary sale recipient.
                 *  @dev            Caller should be authorized to set primary sales info.
                 *                  See {_canSetPrimarySaleRecipient}.
                 *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                 *
                 *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                 */
                function setPrimarySaleRecipient(address _saleRecipient) external override {
                    if (!_canSetPrimarySaleRecipient()) {
                        revert PrimarySaleUnauthorized();
                    }
                    _setupPrimarySaleRecipient(_saleRecipient);
                }
                /// @dev Lets a contract admin set the recipient for all primary sales.
                function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                    if (_saleRecipient == address(0)) {
                        revert PrimarySaleInvalidRecipient(_saleRecipient);
                    }
                    recipient = _saleRecipient;
                    emit PrimarySaleRecipientUpdated(_saleRecipient);
                }
                /// @dev Returns whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IRoyalty.sol";
            /**
             *  @title   Royalty
             *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about royalty fees, if desired.
             *
             *  @dev     The `Royalty` contract is ERC2981 compliant.
             */
            abstract contract RoyaltyMigration is IRoyalty {
                /// @dev The sender is not authorized to perform the action
                error RoyaltyUnauthorized();
                /// @dev The recipient is invalid
                error RoyaltyInvalidRecipient(address recipient);
                /// @dev The fee bps exceeded the max value
                error RoyaltyExceededMaxFeeBps(uint256 max, uint256 actual);
                /// @dev The (default) address that receives all royalty value.
                address internal royaltyRecipient;
                /// @dev The (default) % of a sale to take as royalty (in basis points).
                uint16 internal royaltyBps;
                /// @dev Token ID => royalty recipient and bps for token
                mapping(uint256 => RoyaltyInfo) internal royaltyInfoForToken;
                /**
                 *  @notice   View royalty info for a given token and sale price.
                 *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                 *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                 *  @param salePrice        Sale price of the token.
                 *
                 *  @return receiver        Address of royalty recipient account.
                 *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                 */
                function royaltyInfo(
                    uint256 tokenId,
                    uint256 salePrice
                ) external view virtual override returns (address receiver, uint256 royaltyAmount) {
                    (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                    receiver = recipient;
                    royaltyAmount = (salePrice * bps) / 10_000;
                }
                /**
                 *  @notice          View royalty info for a given token.
                 *  @dev             Returns royalty recipient and bps for `_tokenId`.
                 *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                 */
                function getRoyaltyInfoForToken(uint256 _tokenId) public view virtual returns (address, uint16);
                /**
                 *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                 */
                function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                    return (royaltyRecipient, uint16(royaltyBps));
                }
                /**
                 *  @notice         Updates default royalty recipient and bps.
                 *  @dev            Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                 *
                 *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                 *  @param _royaltyBps         Updated royalty bps.
                 */
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert RoyaltyUnauthorized();
                    }
                    _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                }
                /// @dev Lets a contract admin update the default royalty recipient and bps.
                function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                    if (_royaltyBps > 10_000) {
                        revert RoyaltyExceededMaxFeeBps(10_000, _royaltyBps);
                    }
                    royaltyRecipient = _royaltyRecipient;
                    royaltyBps = uint16(_royaltyBps);
                    emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                }
                /**
                 *  @notice         Updates default royalty recipient and bps for a particular token.
                 *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                 *
                 *  @param _recipient   Address to be set as royalty recipient for given token Id.
                 *  @param _bps         Updated royalty bps for the token Id.
                 */
                function setRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert RoyaltyUnauthorized();
                    }
                    _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                function _setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal {
                    if (_bps > 10_000) {
                        revert RoyaltyExceededMaxFeeBps(10_000, _bps);
                    }
                    royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                    emit RoyaltyForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Returns whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimCondition {
                /**
                 *  @notice The criteria that make up a claim condition.
                 *
                 *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                 *                                        The same claim condition applies until the `startTimestamp`
                 *                                        of the next claim condition.
                 *
                 *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                 *                                        the claim condition.
                 *
                 *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                 *                                        under the claim condition.
                 *
                 *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                 *
                 *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                 *                                        condition.
                 *
                 *  @param pricePerToken                  The price required to pay per token claimed.
                 *
                 *  @param currency                       The currency in which the `pricePerToken` must be paid.
                 *
                 *  @param metadata                       Claim condition metadata.
                 */
                struct ClaimCondition {
                    uint256 startTimestamp;
                    uint256 maxClaimableSupply;
                    uint256 supplyClaimed;
                    uint256 quantityLimitPerWallet;
                    bytes32 merkleRoot;
                    uint256 pricePerToken;
                    address currency;
                    string metadata;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./IClaimCondition.sol";
            /**
             *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimConditionMultiPhase is IClaimCondition {
                /**
                 *  @notice The set of all claim conditions, at any given moment.
                 *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                 *
                 *  @param currentStartId           The uid for the first claim condition amongst the current set of
                 *                                  claim conditions. The uid for each next claim condition is one
                 *                                  more than the previous claim condition's uid.
                 *
                 *  @param count                    The total number of phases / claim conditions in the list
                 *                                  of claim conditions.
                 *
                 *  @param conditions                   The claim conditions at a given uid. Claim conditions
                 *                                  are ordered in an ascending order by their `startTimestamp`.
                 *
                 *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                 */
                struct ClaimConditionList {
                    uint256 currentStartId;
                    uint256 count;
                    mapping(uint256 => ClaimCondition) conditions;
                    mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *  for you contract.
             *
             *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            interface IContractMetadata {
                /// @dev Returns the metadata URI of the contract.
                function contractURI() external view returns (string memory);
                /**
                 *  @dev Sets contract URI for the storefront-level metadata of the contract.
                 *       Only module admin can call this function.
                 */
                function setContractURI(string calldata _uri) external;
                /// @dev Emitted when the contract URI is updated.
                event ContractURIUpdated(string prevURI, string newURI);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./IClaimConditionMultiPhase.sol";
            /**
             *  The interface `IDrop1155` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IDrop1155 is IClaimConditionMultiPhase {
                /**
                 *  @param proof Proof of concerned wallet's inclusion in an allowlist.
                 *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                 *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                 *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                 */
                struct AllowlistProof {
                    bytes32[] proof;
                    uint256 quantityLimitPerWallet;
                    uint256 pricePerToken;
                    address currency;
                }
                /// @notice Emitted when tokens are claimed.
                event TokensClaimed(
                    uint256 indexed claimConditionIndex,
                    address indexed claimer,
                    address indexed receiver,
                    uint256 tokenId,
                    uint256 quantityClaimed
                );
                /// @notice Emitted when the contract's claim conditions are updated.
                event ClaimConditionsUpdated(uint256 indexed tokenId, ClaimCondition[] claimConditions, bool resetEligibility);
                /**
                 *  @notice Lets an account claim a given quantity of NFTs.
                 *
                 *  @param receiver                       The receiver of the NFTs to claim.
                 *  @param tokenId                        The tokenId of the NFT to claim.
                 *  @param quantity                       The quantity of NFTs to claim.
                 *  @param currency                       The currency in which to pay for the claim.
                 *  @param pricePerToken                  The price per token to pay for the claim.
                 *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                 *                                        of the claim conditions that apply.
                 *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                 */
                function claim(
                    address receiver,
                    uint256 tokenId,
                    uint256 quantity,
                    address currency,
                    uint256 pricePerToken,
                    AllowlistProof calldata allowlistProof,
                    bytes memory data
                ) external payable;
                /**
                 *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                 *
                 *  @param tokenId                  The token ID for which to set mint conditions.
                 *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                 *
                 *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                 *                                  in the new claim conditions being set.
                 *
                 */
                function setClaimConditions(uint256 tokenId, ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            interface ILazyMint {
                /// @dev Emitted when tokens are lazy minted.
                event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                /**
                 *  @notice Lazy mints a given amount of NFTs.
                 *
                 *  @param amount           The number of NFTs to lazy mint.
                 *
                 *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *
                 *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                 *
                 *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 amount,
                    string calldata baseURIForTokens,
                    bytes calldata extraData
                ) external returns (uint256 batchId);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *  information about who the contract's owner is.
             */
            interface IOwnable {
                /// @dev Returns the owner of the contract.
                function owner() external view returns (address);
                /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                function setOwner(address _newOwner) external;
                /// @dev Emitted when a new Owner is set.
                event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             * @dev External interface of AccessControl declared to support ERC165 detection.
             */
            interface IPermissions {
                /**
                 * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                 *
                 * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                 * {RoleAdminChanged} not being emitted signaling this.
                 *
                 * _Available since v3.1._
                 */
                event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                /**
                 * @dev Emitted when `account` is granted `role`.
                 *
                 * `sender` is the account that originated the contract call, an admin role
                 * bearer except when using {AccessControl-_setupRole}.
                 */
                event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Emitted when `account` is revoked `role`.
                 *
                 * `sender` is the account that originated the contract call:
                 *   - if using `revokeRole`, it is the admin role bearer
                 *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                 */
                event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Returns `true` if `account` has been granted `role`.
                 */
                function hasRole(bytes32 role, address account) external view returns (bool);
                /**
                 * @dev Returns the admin role that controls `role`. See {grantRole} and
                 * {revokeRole}.
                 *
                 * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                 */
                function getRoleAdmin(bytes32 role) external view returns (bytes32);
                /**
                 * @dev Grants `role` to `account`.
                 *
                 * If `account` had not been already granted `role`, emits a {RoleGranted}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function grantRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from `account`.
                 *
                 * If `account` had been granted `role`, emits a {RoleRevoked} event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function revokeRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from the calling account.
                 *
                 * Roles are often managed via {grantRole} and {revokeRole}: this function's
                 * purpose is to provide a mechanism for accounts to lose their privileges
                 * if they are compromised (such as when a trusted device is misplaced).
                 *
                 * If the calling account had been granted `role`, emits a {RoleRevoked}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must be `account`.
                 */
                function renounceRole(bytes32 role, address account) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./IPermissions.sol";
            /**
             * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
             */
            interface IPermissionsEnumerable is IPermissions {
                /**
                 * @dev Returns one of the accounts that have `role`. `index` must be a
                 * value between 0 and {getRoleMemberCount}, non-inclusive.
                 *
                 * Role bearers are not sorted in any particular way, and their ordering may
                 * change at any point.
                 *
                 * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                 * you perform all queries on the same block. See the following
                 * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                 * for more information.
                 */
                function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                /**
                 * @dev Returns the number of accounts that have `role`. Can be used
                 * together with {getRoleMember} to enumerate all bearers of a role.
                 */
                function getRoleMemberCount(bytes32 role) external view returns (uint256);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *  primary sales, if desired.
             */
            interface IPrimarySale {
                /// @dev The adress that receives all primary sales value.
                function primarySaleRecipient() external view returns (address);
                /// @dev Lets a module admin set the default recipient of all primary sales.
                function setPrimarySaleRecipient(address _saleRecipient) external;
                /// @dev Emitted when a new sale recipient is set.
                event PrimarySaleRecipientUpdated(address indexed recipient);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "../../eip/interface/IERC2981.sol";
            /**
             *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about royalty fees, if desired.
             *
             *  The `Royalty` contract is ERC2981 compliant.
             */
            interface IRoyalty is IERC2981 {
                struct RoyaltyInfo {
                    address recipient;
                    uint256 bps;
                }
                /// @dev Returns the royalty recipient and fee bps.
                function getDefaultRoyaltyInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the royalty bps and recipient.
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                function setRoyaltyInfoForToken(uint256 tokenId, address recipient, uint256 bps) external;
                /// @dev Returns the royalty recipient for a particular token Id.
                function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                /// @dev Emitted when royalty info is updated.
                event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                /// @dev Emitted when royalty recipient for tokenId is set
                event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
            pragma solidity ^0.8.0;
            import "../../../../../eip/interface/IERC20.sol";
            import { Address } from "@openzeppelin/contracts/utils/Address.sol";
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using Address for address;
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    require(
                        (value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender) + value;
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    unchecked {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                        uint256 newAllowance = oldAllowance - value;
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                    }
                }
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) {
                        // Return data is optional
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            interface IWETH {
                function deposit() external payable;
                function withdraw(uint256 amount) external;
                function transfer(address to, uint256 value) external returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            import "./interface/IPlatformFee_V1.sol";
            /**
             *  @title   Platform Fee
             *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about platform fees, if desired.
             */
            abstract contract PlatformFee is IPlatformFee {
                /// @dev The sender is not authorized to perform the action
                error PlatformFeeUnauthorized();
                /// @dev The recipient is invalid
                error PlatformFeeInvalidRecipient(address recipient);
                /// @dev The fee bps exceeded the max value
                error PlatformFeeExceededMaxFeeBps(uint256 max, uint256 actual);
                /// @dev The address that receives all platform fees from all sales.
                address private platformFeeRecipient;
                /// @dev The % of primary sales collected as platform fees.
                uint16 private platformFeeBps;
                /// @dev Returns the platform fee recipient and bps.
                function getPlatformFeeInfo() public view override returns (address, uint16) {
                    return (platformFeeRecipient, uint16(platformFeeBps));
                }
                /**
                 *  @notice         Updates the platform fee recipient and bps.
                 *  @dev            Caller should be authorized to set platform fee info.
                 *                  See {_canSetPlatformFeeInfo}.
                 *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                 *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                 *  @param _platformFeeBps         Updated platformFeeBps.
                 */
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                    if (!_canSetPlatformFeeInfo()) {
                        revert PlatformFeeUnauthorized();
                    }
                    _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Sets the platform fee recipient and bps
                function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                    if (_platformFeeBps > 10_000) {
                        revert PlatformFeeExceededMaxFeeBps(10_000, _platformFeeBps);
                    }
                    if (_platformFeeRecipient == address(0)) {
                        revert PlatformFeeInvalidRecipient(_platformFeeRecipient);
                    }
                    platformFeeBps = uint16(_platformFeeBps);
                    platformFeeRecipient = _platformFeeRecipient;
                    emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Returns whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about platform fees, if desired.
             */
            interface IPlatformFee {
                /// @dev Returns the platform fee bps and recipient.
                function getPlatformFeeInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the fees on primary sales.
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                /// @dev Emitted when fee on primary sales is updated.
                event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            // Helper interfaces
            import { IWETH } from "../infra/interface/IWETH.sol";
            import { SafeERC20, IERC20 } from "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol";
            library CurrencyTransferLib {
                using SafeERC20 for IERC20;
                error CurrencyTransferLibMismatchedValue(uint256 expected, uint256 actual);
                error CurrencyTransferLibFailedNativeTransfer(address recipient, uint256 value);
                /// @dev The address interpreted as native token of the chain.
                address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /// @dev Transfers a given amount of currency.
                function transferCurrency(address _currency, address _from, address _to, uint256 _amount) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        safeTransferNativeToken(_to, _amount);
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfers a given amount of currency. (With native token wrapping)
                function transferCurrencyWithWrapper(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount,
                    address _nativeTokenWrapper
                ) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        if (_from == address(this)) {
                            // withdraw from weth then transfer withdrawn native token to recipient
                            IWETH(_nativeTokenWrapper).withdraw(_amount);
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        } else if (_to == address(this)) {
                            // store native currency in weth
                            if (_amount != msg.value) {
                                revert CurrencyTransferLibMismatchedValue(msg.value, _amount);
                            }
                            IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                        } else {
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        }
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal {
                    if (_from == _to) {
                        return;
                    }
                    if (_from == address(this)) {
                        IERC20(_currency).safeTransfer(_to, _amount);
                    } else {
                        IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                    }
                }
                /// @dev Transfers `amount` of native token to `to`.
                function safeTransferNativeToken(address to, uint256 value) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    if (!success) {
                        revert CurrencyTransferLibFailedNativeTransfer(to, value);
                    }
                }
                /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                function safeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    if (!success) {
                        IWETH(_nativeTokenWrapper).deposit{ value: value }();
                        IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                    }
                }
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            /// @author OpenZeppelin, thirdweb
            library MerkleProof {
                function verify(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool, uint256) {
                    bytes32 computedHash = leaf;
                    uint256 index = 0;
                    for (uint256 i = 0; i < proof.length; i++) {
                        index *= 2;
                        bytes32 proofElement = proof[i];
                        if (computedHash <= proofElement) {
                            // Hash(current computed hash + current element of the proof)
                            computedHash = _efficientHash(computedHash, proofElement);
                        } else {
                            // Hash(current element of the proof + current computed hash)
                            computedHash = _efficientHash(proofElement, computedHash);
                            index += 1;
                        }
                    }
                    // Check if the computed hash (root) is equal to the provided root
                    return (computedHash == root, index);
                }
                /**
                 * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
                 */
                function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, a)
                        mstore(0x20, b)
                        value := keccak256(0x00, 0x40)
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /// @author thirdweb
            /**
             * @dev String operations.
             */
            library Strings {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    // Inspired by OraclizeAPI's implementation - MIT licence
                    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                    if (value == 0) {
                        return "0";
                    }
                    uint256 temp = value;
                    uint256 digits;
                    while (temp != 0) {
                        digits++;
                        temp /= 10;
                    }
                    bytes memory buffer = new bytes(digits);
                    while (value != 0) {
                        digits -= 1;
                        buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                        value /= 10;
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    if (value == 0) {
                        return "0x00";
                    }
                    uint256 temp = value;
                    uint256 length = 0;
                    while (temp != 0) {
                        length++;
                        temp >>= 8;
                    }
                    return toHexString(value, length);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    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_SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    return string(buffer);
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
                /// and the alphabets are capitalized conditionally according to
                /// https://eips.ethereum.org/EIPS/eip-55
                function toHexStringChecksummed(address value) internal pure returns (string memory str) {
                    str = toHexString(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                        let o := add(str, 0x22)
                        let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                        let t := shl(240, 136) // `0b10001000 << 240`
                        for {
                            let i := 0
                        } 1 {
                        } {
                            mstore(add(i, i), mul(t, byte(i, hashed)))
                            i := add(i, 1)
                            if eq(i, 20) {
                                break
                            }
                        }
                        mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                        o := add(o, 0x20)
                        mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
                function toHexString(address value) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        str := mload(0x40)
                        // Allocate the memory.
                        // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                        // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                        // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                        mstore(0x40, add(str, 0x80))
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        str := add(str, 2)
                        mstore(str, 40)
                        let o := add(str, 0x20)
                        mstore(add(o, 40), 0)
                        value := shl(96, value)
                        // We write the string from rightmost digit to leftmost digit.
                        // The following is essentially a do-while loop that also handles the zero case.
                        for {
                            let i := 0
                        } 1 {
                        } {
                            let p := add(o, add(i, i))
                            let temp := byte(i, value)
                            mstore8(add(p, 1), mload(and(temp, 15)))
                            mstore8(p, mload(shr(4, temp)))
                            i := add(i, 1)
                            if eq(i, 20) {
                                break
                            }
                        }
                    }
                }
                /// @dev Returns the hex encoded string from the raw bytes.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexString(bytes memory raw) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(raw);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hex encoded string from the raw bytes.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let length := mload(raw)
                        str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                        mstore(str, add(length, length)) // Store the length of the output.
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        let o := add(str, 0x20)
                        let end := add(raw, length)
                        for {
                        } iszero(eq(raw, end)) {
                        } {
                            raw := add(raw, 1)
                            mstore8(add(o, 1), mload(and(mload(raw), 15)))
                            mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                            o := add(o, 2)
                        }
                        mstore(o, 0) // Zeroize the slot after the string.
                        mstore(0x40, add(o, 0x20)) // Allocate the memory.
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.11;
            import "../lib/MerkleProof.sol";
            import "../eip/interface/IERC1155.sol";
            interface INextId {
                function nextTokenIdToMint() external view returns (uint256);
            }
            abstract contract TokenMigrateERC1155 {
                /// @dev The sender is not authorized to perform the action
                error TokenMigrateUnauthorized();
                /// @dev Token is not eligible for migration
                error TokenMigrateInvalidTokenId(uint256 tokenId);
                /// @dev Invalid proofs to claim the token ownership for id
                error TokenMigrateInvalidProof(address tokenOwner, uint256 tokenId);
                /// @dev Token is already migrated
                error TokenMigrateAlreadyMigrated(address owner, uint256 tokenId);
                /*///////////////////////////////////////////////////////////////
                                        State Variables
                //////////////////////////////////////////////////////////////*/
                /// @notice The merkle root contianing token ownership information.
                bytes32 private ownershipMerkleRoot;
                /// @notice The address of the original token contract.
                address internal _originalContract;
                /// @notice A mapping from ownership id to the amount claimed.
                mapping(uint256 => uint256) private _amountClaimed;
                /*///////////////////////////////////////////////////////////////
                                    External/Public Functions
                //////////////////////////////////////////////////////////////*/
                /// @notice Migrates a token via proving inclusion in the merkle root.
                /// @dev Assumption: tokens on the original contract are non-transferrable.
                function migrate(
                    address _tokenOwner,
                    uint256 _tokenId,
                    uint256 _proofMaxQuantity,
                    bytes32[] calldata _proof
                ) external {
                    // if tokenId doesn't exist in the original contract, then revert
                    // original contract is already frozen, no more new token will be minted after this migration has been setup
                    if (_tokenId >= INextId(_originalContract).nextTokenIdToMint()) {
                        revert TokenMigrateInvalidTokenId(_tokenId);
                    }
                    uint256 id = _ownershipId(_tokenOwner, _tokenId);
                    // Check if the total tokens owed have not already been claimed
                    if (_amountClaimed[id] >= _proofMaxQuantity) {
                        revert TokenMigrateAlreadyMigrated(_tokenOwner, _tokenId);
                    }
                    // Verify that the proof is valid
                    bool isValidProof;
                    (isValidProof, ) = MerkleProof.verify(
                        _proof,
                        _merkleRoot(),
                        keccak256(abi.encodePacked(_tokenId, _tokenOwner, _proofMaxQuantity))
                    );
                    if (!isValidProof) {
                        revert TokenMigrateInvalidProof(_tokenOwner, _tokenId);
                    }
                    // Send the difference to the token owner
                    uint256 _amount = _proofMaxQuantity - _amountClaimed[id];
                    // Mark token ownership as claimed
                    _amountClaimed[id] = _proofMaxQuantity;
                    // Mint token to token owner
                    _mintMigratedTokens(_tokenOwner, _tokenId, _amount);
                }
                /// @notice Sets the merkle root containing token ownership information.
                function setMerkleRoot(bytes32 _merkleRoot) external virtual {
                    if (!_canSetMerkleRoot()) {
                        revert TokenMigrateUnauthorized();
                    }
                    _setupMerkleRoot(_merkleRoot);
                }
                /*///////////////////////////////////////////////////////////////
                                    Internal Functions
                //////////////////////////////////////////////////////////////*/
                /// @notice Returns bitmap id for a particular token ownership claim.
                function _ownershipId(address _tokenOwner, uint256 _tokenId) internal pure virtual returns (uint256) {
                    return uint(keccak256(abi.encodePacked(_tokenOwner, _tokenId)));
                }
                /// @notice Returns the merkle root containing token ownership information.
                function _merkleRoot() internal view virtual returns (bytes32) {
                    return ownershipMerkleRoot;
                }
                /// @notice Sets up the original token contract address.
                function _setupOriginalContract(address __originalContract) internal virtual {
                    _originalContract = __originalContract;
                }
                /// @notice Sets up the merkle root containing token ownership information.
                function _setupMerkleRoot(bytes32 _merkleRoot) internal virtual {
                    ownershipMerkleRoot = _merkleRoot;
                }
                /*///////////////////////////////////////////////////////////////
                                    Unimplemented Functions
                //////////////////////////////////////////////////////////////*/
                /// @notice Mints migrated token to token owner.
                function _mintMigratedTokens(address _tokenOwner, uint256 _tokenId, uint256 _amount) internal virtual;
                /// @notice Returns whether merkle root can be set in the given execution context.
                function _canSetMerkleRoot() internal virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.11;
            /// @author thirdweb
            //   $$\\     $$\\       $$\\                 $$\\                         $$\\
            //   $$ |    $$ |      \\__|                $$ |                        $$ |
            // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
            // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
            //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
            //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
            //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
            //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
            //  ==========  External imports    ==========
            import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
            import "@openzeppelin/contracts/utils/Multicall.sol";
            import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
            //  ==========  Internal imports    ==========
            import "../../lib/CurrencyTransferLib.sol";
            //  ==========  Features    ==========
            import "../../legacy-contracts/extension/PlatformFee_V1.sol";
            import "../../extension/ContractMetadata.sol";
            import "../../extension/RoyaltyMigration.sol";
            import "../../extension/PrimarySale.sol";
            import "../../extension/Ownable.sol";
            import "../../extension/LazyMint.sol";
            import "../../extension/PermissionsEnumerable.sol";
            import "../../extension/Drop1155.sol";
            import "../TokenMigrateERC1155.sol";
            contract DropERC1155M is
                Initializable,
                ContractMetadata,
                PlatformFee,
                RoyaltyMigration,
                PrimarySale,
                Ownable,
                LazyMint,
                PermissionsEnumerable,
                Drop1155,
                Multicall,
                ERC1155Upgradeable,
                TokenMigrateERC1155
            {
                using StringsUpgradeable for uint256;
                /// @dev Unable to transfer the token due to missing role
                error DropTransferRestricted(address from, address to);
                /// @dev Invalid msg.value
                error DropInvalidMsgValue(uint256 expected, uint256 actual);
                /// @dev token owner or approved
                error DropNotApprovedOrOwner(address sender);
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                // Token name
                string public name;
                // Token symbol
                string public symbol;
                /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                bytes32 private constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
                /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");
                /// @dev Only METADATA_ROLE holders can reveal the URI for a batch of delayed reveal NFTs, and update batch metadata.
                bytes32 private constant METADATA_ROLE = keccak256("METADATA_ROLE");
                /// @dev Only MIGRATION_ROLE holders for setting migration merkle root
                bytes32 private constant MIGRATION_ROLE = keccak256("MIGRATION_ROLE");
                /// @dev Next tokenId that was to be claimed on original contract.
                uint256 private migratedNextTokenId;
                /*///////////////////////////////////////////////////////////////
                                            Mappings
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public totalSupply;
                /// @dev Mapping from token ID => maximum possible total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public maxTotalSupply;
                /*///////////////////////////////////////////////////////////////
                                           Events
                //////////////////////////////////////////////////////////////*/
                /// @dev Emitted when the global max supply of a token is updated.
                event MaxTotalSupplyUpdated(uint256 tokenId, uint256 maxTotalSupply);
                /*///////////////////////////////////////////////////////////////
                                Constructor + initializer logic
                //////////////////////////////////////////////////////////////*/
                constructor() initializer {}
                /// @dev Initializes the contract, like a constructor.
                function initialize(
                    address _defaultAdmin,
                    address __originalContract,
                    bytes32 _ownershipMerkleRoot,
                    string memory _contractURI
                ) external initializer {
                    // Initialize inherited contracts, most base-like -> most derived.
                    __ERC1155_init_unchained("");
                    name = DropERC1155M(__originalContract).name();
                    symbol = DropERC1155M(__originalContract).symbol();
                    {
                        _setupOriginalContract(__originalContract);
                        _setupMerkleRoot(_ownershipMerkleRoot);
                        uint256 _nextId = DropERC1155M(__originalContract).nextTokenIdToMint();
                        nextTokenIdToLazyMint = _nextId;
                        migratedNextTokenId = _nextId;
                        for (uint256 i = 0; i < _nextId; i++) {
                            maxTotalSupply[i] = DropERC1155M(__originalContract).maxTotalSupply(i);
                        }
                        (address royaltyRecipient, uint16 royaltyBps) = DropERC1155M(__originalContract).getDefaultRoyaltyInfo();
                        (address platformFeeRecipient, uint256 platformFeeBps) = DropERC1155M(__originalContract)
                            .getPlatformFeeInfo();
                        address primarySaleRecipient = DropERC1155M(__originalContract).primarySaleRecipient();
                        if (platformFeeRecipient != address(0)) {
                            _setupPlatformFeeInfo(platformFeeRecipient, platformFeeBps);
                        }
                        if (royaltyRecipient != address(0)) {
                            _setupDefaultRoyaltyInfo(royaltyRecipient, royaltyBps);
                        }
                        if (primarySaleRecipient != address(0)) {
                            _setupPrimarySaleRecipient(primarySaleRecipient);
                        }
                    }
                    _setupContractURI(_contractURI);
                    _setupOwner(_defaultAdmin);
                    _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                    _setupRole(MINTER_ROLE, _defaultAdmin);
                    _setupRole(TRANSFER_ROLE, address(0));
                    _setupRole(METADATA_ROLE, _defaultAdmin);
                    _setRoleAdmin(METADATA_ROLE, METADATA_ROLE);
                    _setupRole(MIGRATION_ROLE, _defaultAdmin);
                    _setRoleAdmin(MIGRATION_ROLE, MIGRATION_ROLE);
                }
                /*///////////////////////////////////////////////////////////////
                                    Migration functions
                //////////////////////////////////////////////////////////////*/
                /// @notice Returns whether merkle root can be set in the given execution context.
                function _canSetMerkleRoot() internal virtual override returns (bool) {
                    return hasRole(MIGRATION_ROLE, msg.sender);
                }
                /// @notice Mints migrated token to token owner.
                function _mintMigratedTokens(address _tokenOwner, uint256 _tokenId, uint256 _amount) internal virtual override {
                    _checkMaxTotalSupply(_tokenId, _amount);
                    _mint(_tokenOwner, _tokenId, _amount, "");
                }
                /*///////////////////////////////////////////////////////////////
                                    ERC 165 / 1155 / 2981 logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Returns the uri for a given tokenId.
                function uri(uint256 _tokenId) public view override returns (string memory) {
                    if (_tokenId < migratedNextTokenId) {
                        return ERC1155Upgradeable(_originalContract).uri(_tokenId);
                    }
                    string memory batchUri = _getBaseURI(_tokenId);
                    return string(abi.encodePacked(batchUri, _tokenId.toString()));
                }
                /// @dev See ERC 165
                function supportsInterface(
                    bytes4 interfaceId
                ) public view virtual override(ERC1155Upgradeable, IERC165) returns (bool) {
                    return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                }
                /*///////////////////////////////////////////////////////////////
                                    Setter functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets a module admin set a max total supply for token.
                function setMaxTotalSupply(uint256 _tokenId, uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                    maxTotalSupply[_tokenId] = _maxTotalSupply;
                    emit MaxTotalSupplyUpdated(_tokenId, _maxTotalSupply);
                }
                /**
                 * @notice Updates the base URI for a batch of tokens.
                 *
                 * @param _index Index of the desired batch in batchIds array.
                 * @param _uri   the new base URI for the batch.
                 */
                function updateBatchBaseURI(uint256 _index, string calldata _uri) external onlyRole(METADATA_ROLE) {
                    uint256 batchId = getBatchIdAtIndex(_index);
                    _setBaseURI(batchId, _uri);
                }
                /*///////////////////////////////////////////////////////////////
                                    Internal functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address,
                    uint256 _quantity,
                    address,
                    uint256,
                    AllowlistProof calldata,
                    bytes memory
                ) internal view override {
                    _checkMaxTotalSupply(_tokenId, _quantity);
                }
                function _checkMaxTotalSupply(uint256 _tokenId, uint256 _quantity) internal view {
                    if (maxTotalSupply[_tokenId] != 0 && totalSupply[_tokenId] + _quantity > maxTotalSupply[_tokenId]) {
                        revert DropExceedMaxSupply();
                    }
                }
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal override {
                    if (_pricePerToken == 0) {
                        if (msg.value != 0) {
                            revert DropInvalidMsgValue(0, msg.value);
                        }
                        return;
                    }
                    (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                    address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
                    uint256 totalPrice = _quantityToClaim * _pricePerToken;
                    uint256 platformFees = (totalPrice * platformFeeBps) / 10_000;
                    if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                        if (msg.value != totalPrice) {
                            revert DropInvalidMsgValue(totalPrice, msg.value);
                        }
                    } else {
                        if (msg.value != 0) {
                            revert DropInvalidMsgValue(0, msg.value);
                        }
                    }
                    CurrencyTransferLib.transferCurrency(_currency, msg.sender, platformFeeRecipient, platformFees);
                    CurrencyTransferLib.transferCurrency(_currency, msg.sender, saleRecipient, totalPrice - platformFees);
                }
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal override {
                    _mint(_to, _tokenId, _quantityBeingClaimed, "");
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Checks whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Checks whether owner can be set in the given execution context.
                function _canSetOwner() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Checks whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Checks whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetClaimConditions() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
                }
                /// @dev Returns whether lazy minting can be done in the given execution context.
                function _canLazyMint() internal view virtual override returns (bool) {
                    return hasRole(MINTER_ROLE, msg.sender);
                }
                /*///////////////////////////////////////////////////////////////
                                    Miscellaneous
                //////////////////////////////////////////////////////////////*/
                /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                function nextTokenIdToMint() external view returns (uint256) {
                    return nextTokenIdToLazyMint;
                }
                /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good)
                function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
                    if (account != msg.sender && !isApprovedForAll(account, msg.sender)) {
                        revert DropNotApprovedOrOwner(msg.sender);
                    }
                    _burnBatch(account, ids, values);
                }
                /**
                 *  @notice          View royalty info for a given token.
                 *  @dev             Returns royalty recipient and bps for `_tokenId`.
                 *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                 */
                function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                    RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                    // if it's a migrated token and royalty has not been overriden yet
                    if (_tokenId < migratedNextTokenId && royaltyForToken.recipient == address(0) && royaltyForToken.bps == 0) {
                        return IRoyalty(_originalContract).getRoyaltyInfoForToken(_tokenId);
                    }
                    return
                        royaltyForToken.recipient == address(0)
                            ? (royaltyRecipient, uint16(royaltyBps))
                            : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                }
                /**
                 * @dev See {ERC1155-_beforeTokenTransfer}.
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual override {
                    super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    if (!hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) {
                        if (!hasRole(TRANSFER_ROLE, from) && !hasRole(TRANSFER_ROLE, to)) {
                            revert DropTransferRestricted(from, to);
                        }
                    }
                    if (from == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] += amounts[i];
                        }
                    }
                    if (to == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] -= amounts[i];
                        }
                    }
                }
                function _dropMsgSender() internal view virtual override returns (address) {
                    return msg.sender;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
            pragma solidity ^0.8.0;
            import "../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                 */
                function royaltyInfo(
                    uint256 tokenId,
                    uint256 salePrice
                ) external view returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
            pragma solidity ^0.8.2;
            import "../../utils/AddressUpgradeable.sol";
            /**
             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
             *
             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
             * case an upgrade adds a module that needs to be initialized.
             *
             * For example:
             *
             * [.hljs-theme-light.nopadding]
             * ```solidity
             * contract MyToken is ERC20Upgradeable {
             *     function initialize() initializer public {
             *         __ERC20_init("MyToken", "MTK");
             *     }
             * }
             *
             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
             *     function initializeV2() reinitializer(2) public {
             *         __ERC20Permit_init("MyToken");
             *     }
             * }
             * ```
             *
             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
             *
             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
             *
             * [CAUTION]
             * ====
             * Avoid leaving a contract uninitialized.
             *
             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * /// @custom:oz-upgrades-unsafe-allow constructor
             * constructor() {
             *     _disableInitializers();
             * }
             * ```
             * ====
             */
            abstract contract Initializable {
                /**
                 * @dev Indicates that the contract has been initialized.
                 * @custom:oz-retyped-from bool
                 */
                uint8 private _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool private _initializing;
                /**
                 * @dev Triggered when the contract has been initialized or reinitialized.
                 */
                event Initialized(uint8 version);
                /**
                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                 * `onlyInitializing` functions can be used to initialize parent contracts.
                 *
                 * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                 * constructor.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier initializer() {
                    bool isTopLevelCall = !_initializing;
                    require(
                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                        "Initializable: contract is already initialized"
                    );
                    _initialized = 1;
                    if (isTopLevelCall) {
                        _initializing = true;
                    }
                    _;
                    if (isTopLevelCall) {
                        _initializing = false;
                        emit Initialized(1);
                    }
                }
                /**
                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                 * used to initialize parent contracts.
                 *
                 * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                 * are added through upgrades and that require initialization.
                 *
                 * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                 * cannot be nested. If one is invoked in the context of another, execution will revert.
                 *
                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                 * a contract, executing them in the right order is up to the developer or operator.
                 *
                 * WARNING: setting the version to 255 will prevent any future reinitialization.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier reinitializer(uint8 version) {
                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                    _initialized = version;
                    _initializing = true;
                    _;
                    _initializing = false;
                    emit Initialized(version);
                }
                /**
                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                 */
                modifier onlyInitializing() {
                    require(_initializing, "Initializable: contract is not initializing");
                    _;
                }
                /**
                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                 * through proxies.
                 *
                 * Emits an {Initialized} event the first time it is successfully executed.
                 */
                function _disableInitializers() internal virtual {
                    require(!_initializing, "Initializable: contract is initializing");
                    if (_initialized != type(uint8).max) {
                        _initialized = type(uint8).max;
                        emit Initialized(type(uint8).max);
                    }
                }
                /**
                 * @dev Returns the highest version that has been initialized. See {reinitializer}.
                 */
                function _getInitializedVersion() internal view returns (uint8) {
                    return _initialized;
                }
                /**
                 * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                 */
                function _isInitializing() internal view returns (bool) {
                    return _initializing;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol)
            pragma solidity ^0.8.0;
            import "./IERC1155Upgradeable.sol";
            import "./IERC1155ReceiverUpgradeable.sol";
            import "./extensions/IERC1155MetadataURIUpgradeable.sol";
            import "../../utils/AddressUpgradeable.sol";
            import "../../utils/ContextUpgradeable.sol";
            import "../../utils/introspection/ERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the basic standard multi-token.
             * See https://eips.ethereum.org/EIPS/eip-1155
             * Originally based on code by Enjin: https://github.com/enjin/erc-1155
             *
             * _Available since v3.1._
             */
            contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
                using AddressUpgradeable for address;
                // Mapping from token ID to account balances
                mapping(uint256 => mapping(address => uint256)) private _balances;
                // Mapping from account to operator approvals
                mapping(address => mapping(address => bool)) private _operatorApprovals;
                // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
                string private _uri;
                /**
                 * @dev See {_setURI}.
                 */
                function __ERC1155_init(string memory uri_) internal onlyInitializing {
                    __ERC1155_init_unchained(uri_);
                }
                function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
                    _setURI(uri_);
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                    return
                        interfaceId == type(IERC1155Upgradeable).interfaceId ||
                        interfaceId == type(IERC1155MetadataURIUpgradeable).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) public view virtual override returns (string memory) {
                    return _uri;
                }
                /**
                 * @dev See {IERC1155-balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
                    require(account != address(0), "ERC1155: address zero is not a valid owner");
                    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 override returns (uint256[] memory) {
                    require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
                    uint256[] memory batchBalances = new uint256[](accounts.length);
                    for (uint256 i = 0; i < accounts.length; ++i) {
                        batchBalances[i] = balanceOf(accounts[i], ids[i]);
                    }
                    return batchBalances;
                }
                /**
                 * @dev See {IERC1155-setApprovalForAll}.
                 */
                function setApprovalForAll(address operator, bool approved) public virtual override {
                    _setApprovalForAll(_msgSender(), operator, approved);
                }
                /**
                 * @dev See {IERC1155-isApprovedForAll}.
                 */
                function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
                    return _operatorApprovals[account][operator];
                }
                /**
                 * @dev See {IERC1155-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner or approved"
                    );
                    _safeTransferFrom(from, to, id, amount, data);
                }
                /**
                 * @dev See {IERC1155-safeBatchTransferFrom}.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner or approved"
                    );
                    _safeBatchTransferFrom(from, to, ids, amounts, data);
                }
                /**
                 * @dev Transfers `amount` 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 `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 amount,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, from, to, id, amount);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, 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.
                 */
                function _safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; ++i) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                        _balances[id][to] += amount;
                    }
                    emit TransferBatch(operator, from, to, ids, amounts);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
                }
                /**
                 * @dev Sets a new URI for all token types, by relying on the token type ID
                 * substitution mechanism
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
                 *
                 * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
                 * URI or any of the amounts in the JSON file at said URI will be replaced by
                 * clients with the token type ID.
                 *
                 * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
                 * interpreted by clients as
                 * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
                 * for token type ID 0x4cce0.
                 *
                 * See {uri}.
                 *
                 * Because these URIs cannot be meaningfully represented by the {URI} event,
                 * this function emits no events.
                 */
                function _setURI(string memory newuri) internal virtual {
                    _uri = newuri;
                }
                /**
                 * @dev Creates `amount` tokens of token 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 amount, bytes memory data) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, address(0), to, id, amount);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function _mintBatch(
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; i++) {
                        _balances[ids[i]][to] += amounts[i];
                    }
                    emit TransferBatch(operator, address(0), to, ids, amounts);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
                }
                /**
                 * @dev Destroys `amount` tokens of token type `id` from `from`
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `from` must have at least `amount` tokens of token type `id`.
                 */
                function _burn(address from, uint256 id, uint256 amount) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    emit TransferSingle(operator, from, address(0), id, amount);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 */
                function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    for (uint256 i = 0; i < ids.length; i++) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                    }
                    emit TransferBatch(operator, from, address(0), ids, amounts);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev Approve `operator` to operate on all of `owner` tokens
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
                    require(owner != operator, "ERC1155: setting approval status for self");
                    _operatorApprovals[owner][operator] = approved;
                    emit ApprovalForAll(owner, operator, approved);
                }
                /**
                 * @dev Hook that is called before any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `ids` and `amounts` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                /**
                 * @dev Hook that is called after any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `id` and `amount` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _afterTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                function _doSafeTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                        }
                    }
                }
                function _doSafeBatchTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                            bytes4 response
                        ) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                        }
                    }
                }
                function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                    uint256[] memory array = new uint256[](1);
                    array[0] = element;
                    return array;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[47] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
                /**
                 * @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 v4.9.0) (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Emitted when `value` tokens of token 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 amount 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 `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * 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 `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 amount, bytes calldata data) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` 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 amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Upgradeable.sol";
            /**
             * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
             * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
                /**
                 * @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 v4.9.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library AddressUpgradeable {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 *
                 * Furthermore, `isContract` will also return true if the target contract within
                 * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                 * which only has an effect at the end of a transaction.
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                 * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                 *
                 * _Available since v4.8._
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    if (success) {
                        if (returndata.length == 0) {
                            // only check isContract if the call was successful and the return data is empty
                            // otherwise we already know that it was a contract
                            require(isContract(target), "Address: call to non-contract");
                        }
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason or using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                function _revert(bytes memory returndata, string memory errorMessage) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            import "../proxy/utils/Initializable.sol";
            /**
             * @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 ContextUpgradeable is Initializable {
                function __Context_init() internal onlyInitializing {
                }
                function __Context_init_unchained() internal onlyInitializing {
                }
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
            pragma solidity ^0.8.0;
            import "./math/MathUpgradeable.sol";
            import "./math/SignedMathUpgradeable.sol";
            /**
             * @dev String operations.
             */
            library StringsUpgradeable {
                bytes16 private constant _SYMBOLS = "0123456789abcdef";
                uint8 private constant _ADDRESS_LENGTH = 20;
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        uint256 length = MathUpgradeable.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), _SYMBOLS))
                            }
                            value /= 10;
                            if (value == 0) break;
                        }
                        return buffer;
                    }
                }
                /**
                 * @dev Converts a `int256` to its ASCII `string` decimal representation.
                 */
                function toString(int256 value) internal pure returns (string memory) {
                    return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.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, MathUpgradeable.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) {
                    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] = _SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    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 keccak256(bytes(a)) == keccak256(bytes(b));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                function __ERC165_init() internal onlyInitializing {
                }
                function __ERC165_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165Upgradeable).interfaceId;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165Upgradeable {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library MathUpgradeable {
                enum Rounding {
                    Down, // Toward negative infinity
                    Up, // Toward infinity
                    Zero // Toward zero
                }
                /**
                 * @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 up instead
                 * of rounding down.
                 */
                function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (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; // Least significant 256 bits of the product
                        uint256 prod1; // Most significant 256 bits of the product
                        assembly {
                            let mm := mulmod(x, y, not(0))
                            prod0 := mul(x, y)
                            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.
                        require(denominator > prod1, "Math: mulDiv overflow");
                        ///////////////////////////////////////////////
                        // 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.
                        // Does not overflow because the denominator cannot be zero at this stage in the function.
                        uint256 twos = denominator & (~denominator + 1);
                        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 (rounding == Rounding.Up && 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 down.
                 *
                 * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 2, rounded down, of a positive value.
                 * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 10, rounded down, of a positive value.
                 * 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 256, rounded down, of a positive value.
                 * 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Standard signed math utilities missing in the Solidity language.
             */
            library SignedMathUpgradeable {
                /**
                 * @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 v4.9.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 *
                 * Furthermore, `isContract` will also return true if the target contract within
                 * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                 * which only has an effect at the end of a transaction.
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                 * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                 *
                 * _Available since v4.8._
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    if (success) {
                        if (returndata.length == 0) {
                            // only check isContract if the call was successful and the return data is empty
                            // otherwise we already know that it was a contract
                            require(isContract(target), "Address: call to non-contract");
                        }
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason or using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                function _revert(bytes memory returndata, string memory errorMessage) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.9.0) (utils/Multicall.sol)
            pragma solidity ^0.8.0;
            import "./Address.sol";
            /**
             * @dev Provides a function to batch together multiple calls in a single external call.
             *
             * _Available since v4.1._
             */
            abstract contract Multicall {
                /**
                 * @dev Receives and executes a batch of function calls on this contract.
                 * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                 */
                function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                    results = new bytes[](data.length);
                    for (uint256 i = 0; i < data.length; i++) {
                        results[i] = Address.functionDelegateCall(address(this), data[i]);
                    }
                    return results;
                }
            }
            

            File 3 of 4: DropERC1155
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.11;
            //  ==========  External imports    ==========
            import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
            //  ==========  Internal imports    ==========
            import "../openzeppelin-presets/metatx/ERC2771ContextUpgradeable.sol";
            import "../lib/CurrencyTransferLib.sol";
            //  ==========  Features    ==========
            import "../extension/ContractMetadata.sol";
            import "../extension/PlatformFee.sol";
            import "../extension/Royalty.sol";
            import "../extension/PrimarySale.sol";
            import "../extension/Ownable.sol";
            import "../extension/LazyMint.sol";
            import "../extension/PermissionsEnumerable.sol";
            import "../extension/Drop1155.sol";
            // OpenSea operator filter
            import "../extension/DefaultOperatorFiltererUpgradeable.sol";
            contract DropERC1155 is
                Initializable,
                ContractMetadata,
                PlatformFee,
                Royalty,
                PrimarySale,
                Ownable,
                LazyMint,
                PermissionsEnumerable,
                Drop1155,
                ERC2771ContextUpgradeable,
                MulticallUpgradeable,
                DefaultOperatorFiltererUpgradeable,
                ERC1155Upgradeable
            {
                using StringsUpgradeable for uint256;
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                // Token name
                string public name;
                // Token symbol
                string public symbol;
                /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                bytes32 private transferRole;
                /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                bytes32 private minterRole;
                /// @dev Max bps in the thirdweb system.
                uint256 private constant MAX_BPS = 10_000;
                /*///////////////////////////////////////////////////////////////
                                            Mappings
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public totalSupply;
                /// @dev Mapping from token ID => maximum possible total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public maxTotalSupply;
                /// @dev Mapping from token ID => the address of the recipient of primary sales.
                mapping(uint256 => address) public saleRecipient;
                /*///////////////////////////////////////////////////////////////
                                           Events
                //////////////////////////////////////////////////////////////*/
                /// @dev Emitted when the global max supply of a token is updated.
                event MaxTotalSupplyUpdated(uint256 tokenId, uint256 maxTotalSupply);
                /// @dev Emitted when the sale recipient for a particular tokenId is updated.
                event SaleRecipientForTokenUpdated(uint256 indexed tokenId, address saleRecipient);
                /*///////////////////////////////////////////////////////////////
                                Constructor + initializer logic
                //////////////////////////////////////////////////////////////*/
                constructor() initializer {}
                /// @dev Initiliazes the contract, like a constructor.
                function initialize(
                    address _defaultAdmin,
                    string memory _name,
                    string memory _symbol,
                    string memory _contractURI,
                    address[] memory _trustedForwarders,
                    address _saleRecipient,
                    address _royaltyRecipient,
                    uint128 _royaltyBps,
                    uint128 _platformFeeBps,
                    address _platformFeeRecipient
                ) external initializer {
                    bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                    bytes32 _minterRole = keccak256("MINTER_ROLE");
                    // Initialize inherited contracts, most base-like -> most derived.
                    __ERC2771Context_init(_trustedForwarders);
                    __ERC1155_init_unchained("");
                    __DefaultOperatorFilterer_init();
                    // Initialize this contract's state.
                    _setupContractURI(_contractURI);
                    _setupOwner(_defaultAdmin);
                    _setOperatorRestriction(true);
                    _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                    _setupRole(_minterRole, _defaultAdmin);
                    _setupRole(_transferRole, _defaultAdmin);
                    _setupRole(_transferRole, address(0));
                    _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                    _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                    _setupPrimarySaleRecipient(_saleRecipient);
                    transferRole = _transferRole;
                    minterRole = _minterRole;
                    name = _name;
                    symbol = _symbol;
                }
                /*///////////////////////////////////////////////////////////////
                                    ERC 165 / 1155 / 2981 logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Returns the uri for a given tokenId.
                function uri(uint256 _tokenId) public view override returns (string memory) {
                    string memory batchUri = _getBaseURI(_tokenId);
                    return string(abi.encodePacked(batchUri, _tokenId.toString()));
                }
                /// @dev See ERC 165
                function supportsInterface(bytes4 interfaceId)
                    public
                    view
                    virtual
                    override(ERC1155Upgradeable, IERC165)
                    returns (bool)
                {
                    return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                }
                /*///////////////////////////////////////////////////////////////
                                    Contract identifiers
                //////////////////////////////////////////////////////////////*/
                function contractType() external pure returns (bytes32) {
                    return bytes32("DropERC1155");
                }
                function contractVersion() external pure returns (uint8) {
                    return uint8(4);
                }
                /*///////////////////////////////////////////////////////////////
                                    Setter functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets a module admin set a max total supply for token.
                function setMaxTotalSupply(uint256 _tokenId, uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                    maxTotalSupply[_tokenId] = _maxTotalSupply;
                    emit MaxTotalSupplyUpdated(_tokenId, _maxTotalSupply);
                }
                /// @dev Lets a contract admin set the recipient for all primary sales.
                function setSaleRecipientForToken(uint256 _tokenId, address _saleRecipient) external onlyRole(DEFAULT_ADMIN_ROLE) {
                    saleRecipient[_tokenId] = _saleRecipient;
                    emit SaleRecipientForTokenUpdated(_tokenId, _saleRecipient);
                }
                /*///////////////////////////////////////////////////////////////
                                    Internal functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address,
                    uint256 _quantity,
                    address,
                    uint256,
                    AllowlistProof calldata,
                    bytes memory
                ) internal view override {
                    require(
                        maxTotalSupply[_tokenId] == 0 || totalSupply[_tokenId] + _quantity <= maxTotalSupply[_tokenId],
                        "exceed max total supply"
                    );
                }
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256 _tokenId,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal override {
                    if (_pricePerToken == 0) {
                        return;
                    }
                    (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                    address _saleRecipient = _primarySaleRecipient == address(0)
                        ? (saleRecipient[_tokenId] == address(0) ? primarySaleRecipient() : saleRecipient[_tokenId])
                        : _primarySaleRecipient;
                    uint256 totalPrice = _quantityToClaim * _pricePerToken;
                    uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                    if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                        if (msg.value != totalPrice) {
                            revert("!Price");
                        }
                    }
                    CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                    CurrencyTransferLib.transferCurrency(_currency, _msgSender(), _saleRecipient, totalPrice - platformFees);
                }
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(
                    address _to,
                    uint256 _tokenId,
                    uint256 _quantityBeingClaimed
                ) internal override {
                    _mint(_to, _tokenId, _quantityBeingClaimed, "");
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether owner can be set in the given execution context.
                function _canSetOwner() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetClaimConditions() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Returns whether lazy minting can be done in the given execution context.
                function _canLazyMint() internal view virtual override returns (bool) {
                    return hasRole(minterRole, _msgSender());
                }
                /// @dev Returns whether operator restriction can be set in the given execution context.
                function _canSetOperatorRestriction() internal virtual override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /*///////////////////////////////////////////////////////////////
                                    Miscellaneous
                //////////////////////////////////////////////////////////////*/
                /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                function nextTokenIdToMint() external view returns (uint256) {
                    return nextTokenIdToLazyMint;
                }
                /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good)
                function burnBatch(
                    address account,
                    uint256[] memory ids,
                    uint256[] memory values
                ) public virtual {
                    require(
                        account == _msgSender() || isApprovedForAll(account, _msgSender()),
                        "ERC1155: caller is not owner nor approved."
                    );
                    _burnBatch(account, ids, values);
                }
                /**
                 * @dev See {ERC1155-_beforeTokenTransfer}.
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual override {
                    super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    // if transfer is restricted on the contract, we still want to allow burning and minting
                    if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                        require(hasRole(transferRole, from) || hasRole(transferRole, to), "restricted to TRANSFER_ROLE holders.");
                    }
                    if (from == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] += amounts[i];
                        }
                    }
                    if (to == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] -= amounts[i];
                        }
                    }
                }
                /// @dev See {ERC1155-setApprovalForAll}
                function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
                    super.setApprovalForAll(operator, approved);
                }
                /**
                 * @dev See {IERC1155-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) public override(ERC1155Upgradeable) onlyAllowedOperator(from) {
                    super.safeTransferFrom(from, to, id, amount, data);
                }
                /**
                 * @dev See {IERC1155-safeBatchTransferFrom}.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) public override(ERC1155Upgradeable) onlyAllowedOperator(from) {
                    super.safeBatchTransferFrom(from, to, ids, amounts, data);
                }
                function _dropMsgSender() internal view virtual override returns (address) {
                    return _msgSender();
                }
                function _msgSender()
                    internal
                    view
                    virtual
                    override(ContextUpgradeable, ERC2771ContextUpgradeable)
                    returns (address sender)
                {
                    return ERC2771ContextUpgradeable._msgSender();
                }
                function _msgData()
                    internal
                    view
                    virtual
                    override(ContextUpgradeable, ERC2771ContextUpgradeable)
                    returns (bytes calldata)
                {
                    return ERC2771ContextUpgradeable._msgData();
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * [EIP](https://eips.ethereum.org/EIPS/eip-165).
             *
             * 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
                 * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             * @title ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            interface IERC20 {
                function totalSupply() external view returns (uint256);
                function balanceOf(address who) external view returns (uint256);
                function allowance(address owner, address spender) external view returns (uint256);
                function transfer(address to, uint256 value) external returns (bool);
                function approve(address spender, uint256 value) external returns (bool);
                function transferFrom(
                    address from,
                    address to,
                    uint256 value
                ) external returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
                event Approval(address indexed owner, address indexed spender, uint256 value);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981 is IERC165 {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  @title   Batch-mint Metadata
             *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
             *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
             *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
             */
            contract BatchMintMetadata {
                /// @dev Largest tokenId of each batch of tokens with the same baseURI.
                uint256[] private batchIds;
                /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                mapping(uint256 => string) private baseURI;
                /**
                 *  @notice         Returns the count of batches of NFTs.
                 *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                 *                  See {batchIds}.
                 */
                function getBaseURICount() public view returns (uint256) {
                    return batchIds.length;
                }
                /**
                 *  @notice         Returns the ID for the batch of tokens the given tokenId belongs to.
                 *  @dev            See {getBaseURICount}.
                 *  @param _index   ID of a token.
                 */
                function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                    if (_index >= getBaseURICount()) {
                        revert("Invalid index");
                    }
                    return batchIds[_index];
                }
                /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            index = i;
                            batchId = indices[i];
                            return (batchId, index);
                        }
                    }
                    revert("Invalid tokenId");
                }
                /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            return baseURI[indices[i]];
                        }
                    }
                    revert("Invalid tokenId");
                }
                /// @dev Sets the base URI for the batch of tokens with the given batchId.
                function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                    baseURI[_batchId] = _baseURI;
                }
                /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                function _batchMintMetadata(
                    uint256 _startId,
                    uint256 _amountToMint,
                    string memory _baseURIForTokens
                ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                    batchId = _startId + _amountToMint;
                    nextTokenIdToMint = batchId;
                    batchIds.push(batchId);
                    baseURI[batchId] = _baseURIForTokens;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IContractMetadata.sol";
            /**
             *  @title   Contract Metadata
             *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *           for you contract.
             *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            abstract contract ContractMetadata is IContractMetadata {
                /// @notice Returns the contract metadata URI.
                string public override contractURI;
                /**
                 *  @notice         Lets a contract admin set the URI for contract-level metadata.
                 *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                 *                  See {_canSetContractURI}.
                 *                  Emits {ContractURIUpdated Event}.
                 *
                 *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function setContractURI(string memory _uri) external override {
                    if (!_canSetContractURI()) {
                        revert("Not authorized");
                    }
                    _setupContractURI(_uri);
                }
                /// @dev Lets a contract admin set the URI for contract-level metadata.
                function _setupContractURI(string memory _uri) internal {
                    string memory prevURI = contractURI;
                    contractURI = _uri;
                    emit ContractURIUpdated(prevURI, _uri);
                }
                /// @dev Returns whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import { OperatorFiltererUpgradeable } from "./OperatorFiltererUpgradeable.sol";
            abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                function __DefaultOperatorFilterer_init() internal {
                    OperatorFiltererUpgradeable.__OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IDrop1155.sol";
            import "../lib/MerkleProof.sol";
            abstract contract Drop1155 is IDrop1155 {
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => the set of all claim conditions, at any given moment, for tokens of the token ID.
                mapping(uint256 => ClaimConditionList) public claimCondition;
                /*///////////////////////////////////////////////////////////////
                                        Drop logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets an account claim tokens.
                function claim(
                    address _receiver,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) public payable virtual override {
                    _beforeClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                    uint256 activeConditionId = getActiveClaimConditionId(_tokenId);
                    verifyClaim(
                        activeConditionId,
                        _dropMsgSender(),
                        _tokenId,
                        _quantity,
                        _currency,
                        _pricePerToken,
                        _allowlistProof
                    );
                    // Update contract state.
                    claimCondition[_tokenId].conditions[activeConditionId].supplyClaimed += _quantity;
                    claimCondition[_tokenId].supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                    // If there's a price, collect price.
                    collectPriceOnClaim(_tokenId, address(0), _quantity, _currency, _pricePerToken);
                    // Mint the relevant NFTs to claimer.
                    transferTokensOnClaim(_receiver, _tokenId, _quantity);
                    emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, _tokenId, _quantity);
                    _afterClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                }
                /// @dev Lets a contract admin set claim conditions.
                function setClaimConditions(
                    uint256 _tokenId,
                    ClaimCondition[] calldata _conditions,
                    bool _resetClaimEligibility
                ) external virtual override {
                    if (!_canSetClaimConditions()) {
                        revert("Not authorized");
                    }
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    uint256 existingStartIndex = conditionList.currentStartId;
                    uint256 existingPhaseCount = conditionList.count;
                    /**
                     *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                     *  by `supplyClaimedByWallet`.
                     */
                    uint256 newStartIndex = existingStartIndex;
                    if (_resetClaimEligibility) {
                        newStartIndex = existingStartIndex + existingPhaseCount;
                    }
                    conditionList.count = _conditions.length;
                    conditionList.currentStartId = newStartIndex;
                    uint256 lastConditionStartTimestamp;
                    for (uint256 i = 0; i < _conditions.length; i++) {
                        require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                        uint256 supplyClaimedAlready = conditionList.conditions[newStartIndex + i].supplyClaimed;
                        if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                            revert("max supply claimed");
                        }
                        conditionList.conditions[newStartIndex + i] = _conditions[i];
                        conditionList.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                        lastConditionStartTimestamp = _conditions[i].startTimestamp;
                    }
                    /**
                     *  Gas refunds (as much as possible)
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                     *
                     *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                     *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                     *  by the conditions in `_conditions`.
                     */
                    if (_resetClaimEligibility) {
                        for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                            delete conditionList.conditions[i];
                        }
                    } else {
                        if (existingPhaseCount > _conditions.length) {
                            for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                delete conditionList.conditions[newStartIndex + i];
                            }
                        }
                    }
                    emit ClaimConditionsUpdated(_tokenId, _conditions, _resetClaimEligibility);
                }
                /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                function verifyClaim(
                    uint256 _conditionId,
                    address _claimer,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof
                ) public view returns (bool isOverride) {
                    ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].conditions[_conditionId];
                    uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                    uint256 claimPrice = currentClaimPhase.pricePerToken;
                    address claimCurrency = currentClaimPhase.currency;
                    if (currentClaimPhase.merkleRoot != bytes32(0)) {
                        (isOverride, ) = MerkleProof.verify(
                            _allowlistProof.proof,
                            currentClaimPhase.merkleRoot,
                            keccak256(
                                abi.encodePacked(
                                    _claimer,
                                    _allowlistProof.quantityLimitPerWallet,
                                    _allowlistProof.pricePerToken,
                                    _allowlistProof.currency
                                )
                            )
                        );
                    }
                    if (isOverride) {
                        claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                            ? _allowlistProof.quantityLimitPerWallet
                            : claimLimit;
                        claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                            ? _allowlistProof.pricePerToken
                            : claimPrice;
                        claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                            ? _allowlistProof.currency
                            : claimCurrency;
                    }
                    uint256 supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                    if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                        revert("!PriceOrCurrency");
                    }
                    if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                        revert("!Qty");
                    }
                    if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                        revert("!MaxSupply");
                    }
                    if (currentClaimPhase.startTimestamp > block.timestamp) {
                        revert("cant claim yet");
                    }
                }
                /// @dev At any given moment, returns the uid for the active claim condition.
                function getActiveClaimConditionId(uint256 _tokenId) public view returns (uint256) {
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    for (uint256 i = conditionList.currentStartId + conditionList.count; i > conditionList.currentStartId; i--) {
                        if (block.timestamp >= conditionList.conditions[i - 1].startTimestamp) {
                            return i - 1;
                        }
                    }
                    revert("!CONDITION.");
                }
                /// @dev Returns the claim condition at the given uid.
                function getClaimConditionById(uint256 _tokenId, uint256 _conditionId)
                    external
                    view
                    returns (ClaimCondition memory condition)
                {
                    condition = claimCondition[_tokenId].conditions[_conditionId];
                }
                /// @dev Returns the supply claimed by claimer for a given conditionId.
                function getSupplyClaimedByWallet(
                    uint256 _tokenId,
                    uint256 _conditionId,
                    address _claimer
                ) public view returns (uint256 supplyClaimedByWallet) {
                    supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                }
                /*////////////////////////////////////////////////////////////////////
                    Optional hooks that can be implemented in the derived contract
                ///////////////////////////////////////////////////////////////////*/
                /// @dev Exposes the ability to override the msg sender.
                function _dropMsgSender() internal virtual returns (address) {
                    return msg.sender;
                }
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /// @dev Runs after every `claim` function call.
                function _afterClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /*///////////////////////////////////////////////////////////////
                    Virtual functions: to be implemented in derived contract
                //////////////////////////////////////////////////////////////*/
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256 _tokenId,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal virtual;
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(
                    address _to,
                    uint256 _tokenId,
                    uint256 _quantityBeingClaimed
                ) internal virtual;
                /// @dev Determine what wallet can update claim conditions
                function _canSetClaimConditions() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/ILazyMint.sol";
            import "./BatchMintMetadata.sol";
            /**
             *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                uint256 internal nextTokenIdToLazyMint;
                /**
                 *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                 *
                 *  @param _amount           The number of NFTs to lazy mint.
                 *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                 *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 _amount,
                    string calldata _baseURIForTokens,
                    bytes calldata _data
                ) public virtual override returns (uint256 batchId) {
                    if (!_canLazyMint()) {
                        revert("Not authorized");
                    }
                    if (_amount == 0) {
                        revert("0 amt");
                    }
                    uint256 startId = nextTokenIdToLazyMint;
                    (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                    emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                    return batchId;
                }
                /// @dev Returns whether lazy minting can be performed in the given execution context.
                function _canLazyMint() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import "./interface/IOperatorFilterToggle.sol";
            abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                bool public operatorRestriction;
                function setOperatorRestriction(bool _restriction) external {
                    require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                    _setOperatorRestriction(_restriction);
                }
                function _setOperatorRestriction(bool _restriction) internal {
                    operatorRestriction = _restriction;
                    emit OperatorRestriction(_restriction);
                }
                function _canSetOperatorRestriction() internal virtual returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.12;
            import "./interface/IOperatorFilterRegistry.sol";
            import "./OperatorFilterToggle.sol";
            abstract contract OperatorFiltererUpgradeable is OperatorFilterToggle {
                error OperatorNotAllowed(address operator);
                IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                    IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) internal {
                    // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                    // will not revert, but the contract will need to be registered with the registry once it is deployed in
                    // order for the modifier to filter addresses.
                    if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                        if (!OPERATOR_FILTER_REGISTRY.isRegistered(address(this))) {
                            if (subscribe) {
                                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                            } else {
                                if (subscriptionOrRegistrantToCopy != address(0)) {
                                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                                } else {
                                    OPERATOR_FILTER_REGISTRY.register(address(this));
                                }
                            }
                        }
                    }
                }
                modifier onlyAllowedOperator(address from) virtual {
                    // Check registry code length to facilitate testing in environments without a deployed registry.
                    if (operatorRestriction) {
                        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                            // Allow spending tokens from addresses with balance
                            // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                            // from an EOA.
                            if (from == msg.sender) {
                                _;
                                return;
                            }
                            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), msg.sender)) {
                                revert OperatorNotAllowed(msg.sender);
                            }
                        }
                    }
                    _;
                }
                modifier onlyAllowedOperatorApproval(address operator) virtual {
                    // Check registry code length to facilitate testing in environments without a deployed registry.
                    if (operatorRestriction) {
                        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                                revert OperatorNotAllowed(operator);
                            }
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IOwnable.sol";
            /**
             *  @title   Ownable
             *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *           information about who the contract's owner is.
             */
            abstract contract Ownable is IOwnable {
                /// @dev Owner of the contract (purpose: OpenSea compatibility)
                address private _owner;
                /// @dev Reverts if caller is not the owner.
                modifier onlyOwner() {
                    if (msg.sender != _owner) {
                        revert("Not authorized");
                    }
                    _;
                }
                /**
                 *  @notice Returns the owner of the contract.
                 */
                function owner() public view override returns (address) {
                    return _owner;
                }
                /**
                 *  @notice Lets an authorized wallet set a new owner for the contract.
                 *  @param _newOwner The address to set as the new owner of the contract.
                 */
                function setOwner(address _newOwner) external override {
                    if (!_canSetOwner()) {
                        revert("Not authorized");
                    }
                    _setupOwner(_newOwner);
                }
                /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                function _setupOwner(address _newOwner) internal {
                    address _prevOwner = _owner;
                    _owner = _newOwner;
                    emit OwnerUpdated(_prevOwner, _newOwner);
                }
                /// @dev Returns whether owner can be set in the given execution context.
                function _canSetOwner() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPermissions.sol";
            import "../lib/TWStrings.sol";
            /**
             *  @title   Permissions
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
             */
            contract Permissions is IPermissions {
                /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                mapping(bytes32 => mapping(address => bool)) private _hasRole;
                /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                mapping(bytes32 => bytes32) private _getRoleAdmin;
                /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                modifier onlyRole(bytes32 role) {
                    _checkRole(role, msg.sender);
                    _;
                }
                /**
                 *  @notice         Checks whether an account has a particular role.
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRole(bytes32 role, address account) public view override returns (bool) {
                    return _hasRole[role][account];
                }
                /**
                 *  @notice         Checks whether an account has a particular role;
                 *                  role restrictions can be swtiched on and off.
                 *
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *                  Role restrictions can be swtiched on and off:
                 *                      - If address(0) has ROLE, then the ROLE restrictions
                 *                        don't apply.
                 *                      - If address(0) does not have ROLE, then the ROLE
                 *                        restrictions will apply.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                    if (!_hasRole[role][address(0)]) {
                        return _hasRole[role][account];
                    }
                    return true;
                }
                /**
                 *  @notice         Returns the admin role that controls the specified role.
                 *  @dev            See {grantRole} and {revokeRole}.
                 *                  To change a role's admin, use {_setRoleAdmin}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                    return _getRoleAdmin[role];
                }
                /**
                 *  @notice         Grants a role to an account, if not previously granted.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleGranted Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account to which the role is being granted.
                 */
                function grantRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    if (_hasRole[role][account]) {
                        revert("Can only grant to non holders");
                    }
                    _setupRole(role, account);
                }
                /**
                 *  @notice         Revokes role from an account.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function revokeRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    _revokeRole(role, account);
                }
                /**
                 *  @notice         Revokes role from the account.
                 *  @dev            Caller must have the `role`, with caller being the same as `account`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function renounceRole(bytes32 role, address account) public virtual override {
                    if (msg.sender != account) {
                        revert("Can only renounce for self");
                    }
                    _revokeRole(role, account);
                }
                /// @dev Sets `adminRole` as `role`'s admin role.
                function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                    bytes32 previousAdminRole = _getRoleAdmin[role];
                    _getRoleAdmin[role] = adminRole;
                    emit RoleAdminChanged(role, previousAdminRole, adminRole);
                }
                /// @dev Sets up `role` for `account`
                function _setupRole(bytes32 role, address account) internal virtual {
                    _hasRole[role][account] = true;
                    emit RoleGranted(role, account, msg.sender);
                }
                /// @dev Revokes `role` from `account`
                function _revokeRole(bytes32 role, address account) internal virtual {
                    _checkRole(role, account);
                    delete _hasRole[role][account];
                    emit RoleRevoked(role, account, msg.sender);
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRole(bytes32 role, address account) internal view virtual {
                    if (!_hasRole[role][account]) {
                        revert(
                            string(
                                abi.encodePacked(
                                    "Permissions: account ",
                                    TWStrings.toHexString(uint160(account), 20),
                                    " is missing role ",
                                    TWStrings.toHexString(uint256(role), 32)
                                )
                            )
                        );
                    }
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                    if (!hasRoleWithSwitch(role, account)) {
                        revert(
                            string(
                                abi.encodePacked(
                                    "Permissions: account ",
                                    TWStrings.toHexString(uint160(account), 20),
                                    " is missing role ",
                                    TWStrings.toHexString(uint256(role), 32)
                                )
                            )
                        );
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPermissionsEnumerable.sol";
            import "./Permissions.sol";
            /**
             *  @title   PermissionsEnumerable
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
             *           Also provides interfaces to view all members with a given role, and total count of members.
             */
            contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                /**
                 *  @notice A data structure to store data of members for a given role.
                 *
                 *  @param index    Current index in the list of accounts that have a role.
                 *  @param members  map from index => address of account that has a role
                 *  @param indexOf  map from address => index which the account has.
                 */
                struct RoleMembers {
                    uint256 index;
                    mapping(uint256 => address) members;
                    mapping(address => uint256) indexOf;
                }
                /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                mapping(bytes32 => RoleMembers) private roleMembers;
                /**
                 *  @notice         Returns the role-member from a list of members for a role,
                 *                  at a given index.
                 *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param index    Index in list of current members for the role.
                 *
                 *  @return member  Address of account that has `role`
                 */
                function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                    uint256 currentIndex = roleMembers[role].index;
                    uint256 check;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            if (check == index) {
                                member = roleMembers[role].members[i];
                                return member;
                            }
                            check += 1;
                        } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                            check += 1;
                        }
                    }
                }
                /**
                 *  @notice         Returns total number of accounts that have a role.
                 *  @dev            Returns `count` of accounts that have `role`.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *
                 *  @return count   Total number of accounts that have `role`
                 */
                function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                    uint256 currentIndex = roleMembers[role].index;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            count += 1;
                        }
                    }
                    if (hasRole(role, address(0))) {
                        count += 1;
                    }
                }
                /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                ///      See {_removeMember}
                function _revokeRole(bytes32 role, address account) internal override {
                    super._revokeRole(role, account);
                    _removeMember(role, account);
                }
                /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                ///      See {_addMember}
                function _setupRole(bytes32 role, address account) internal override {
                    super._setupRole(role, account);
                    _addMember(role, account);
                }
                /// @dev adds `account` to {roleMembers}, for `role`
                function _addMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].index;
                    roleMembers[role].index += 1;
                    roleMembers[role].members[idx] = account;
                    roleMembers[role].indexOf[account] = idx;
                }
                /// @dev removes `account` from {roleMembers}, for `role`
                function _removeMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].indexOf[account];
                    delete roleMembers[role].members[idx];
                    delete roleMembers[role].indexOf[account];
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPlatformFee.sol";
            /**
             *  @title   Platform Fee
             *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about platform fees, if desired.
             */
            abstract contract PlatformFee is IPlatformFee {
                /// @dev The address that receives all platform fees from all sales.
                address private platformFeeRecipient;
                /// @dev The % of primary sales collected as platform fees.
                uint16 private platformFeeBps;
                /// @dev Returns the platform fee recipient and bps.
                function getPlatformFeeInfo() public view override returns (address, uint16) {
                    return (platformFeeRecipient, uint16(platformFeeBps));
                }
                /**
                 *  @notice         Updates the platform fee recipient and bps.
                 *  @dev            Caller should be authorized to set platform fee info.
                 *                  See {_canSetPlatformFeeInfo}.
                 *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                 *
                 *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                 *  @param _platformFeeBps         Updated platformFeeBps.
                 */
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                    if (!_canSetPlatformFeeInfo()) {
                        revert("Not authorized");
                    }
                    _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Lets a contract admin update the platform fee recipient and bps
                function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                    if (_platformFeeBps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    platformFeeBps = uint16(_platformFeeBps);
                    platformFeeRecipient = _platformFeeRecipient;
                    emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Returns whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPrimarySale.sol";
            /**
             *  @title   Primary Sale
             *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *           primary sales, if desired.
             */
            abstract contract PrimarySale is IPrimarySale {
                /// @dev The address that receives all primary sales value.
                address private recipient;
                /// @dev Returns primary sale recipient address.
                function primarySaleRecipient() public view override returns (address) {
                    return recipient;
                }
                /**
                 *  @notice         Updates primary sale recipient.
                 *  @dev            Caller should be authorized to set primary sales info.
                 *                  See {_canSetPrimarySaleRecipient}.
                 *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                 *
                 *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                 */
                function setPrimarySaleRecipient(address _saleRecipient) external override {
                    if (!_canSetPrimarySaleRecipient()) {
                        revert("Not authorized");
                    }
                    _setupPrimarySaleRecipient(_saleRecipient);
                }
                /// @dev Lets a contract admin set the recipient for all primary sales.
                function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                    recipient = _saleRecipient;
                    emit PrimarySaleRecipientUpdated(_saleRecipient);
                }
                /// @dev Returns whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IRoyalty.sol";
            /**
             *  @title   Royalty
             *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about royalty fees, if desired.
             *
             *  @dev     The `Royalty` contract is ERC2981 compliant.
             */
            abstract contract Royalty is IRoyalty {
                /// @dev The (default) address that receives all royalty value.
                address private royaltyRecipient;
                /// @dev The (default) % of a sale to take as royalty (in basis points).
                uint16 private royaltyBps;
                /// @dev Token ID => royalty recipient and bps for token
                mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                /**
                 *  @notice   View royalty info for a given token and sale price.
                 *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                 *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                 *  @param salePrice        Sale price of the token.
                 *
                 *  @return receiver        Address of royalty recipient account.
                 *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    virtual
                    override
                    returns (address receiver, uint256 royaltyAmount)
                {
                    (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                    receiver = recipient;
                    royaltyAmount = (salePrice * bps) / 10_000;
                }
                /**
                 *  @notice          View royalty info for a given token.
                 *  @dev             Returns royalty recipient and bps for `_tokenId`.
                 *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                 */
                function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                    RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                    return
                        royaltyForToken.recipient == address(0)
                            ? (royaltyRecipient, uint16(royaltyBps))
                            : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                }
                /**
                 *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                 */
                function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                    return (royaltyRecipient, uint16(royaltyBps));
                }
                /**
                 *  @notice         Updates default royalty recipient and bps.
                 *  @dev            Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                 *
                 *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                 *  @param _royaltyBps         Updated royalty bps.
                 */
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert("Not authorized");
                    }
                    _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                }
                /// @dev Lets a contract admin update the default royalty recipient and bps.
                function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                    if (_royaltyBps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    royaltyRecipient = _royaltyRecipient;
                    royaltyBps = uint16(_royaltyBps);
                    emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                }
                /**
                 *  @notice         Updates default royalty recipient and bps for a particular token.
                 *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                 *
                 *  @param _recipient   Address to be set as royalty recipient for given token Id.
                 *  @param _bps         Updated royalty bps for the token Id.
                 */
                function setRoyaltyInfoForToken(
                    uint256 _tokenId,
                    address _recipient,
                    uint256 _bps
                ) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert("Not authorized");
                    }
                    _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                function _setupRoyaltyInfoForToken(
                    uint256 _tokenId,
                    address _recipient,
                    uint256 _bps
                ) internal {
                    if (_bps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                    emit RoyaltyForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Returns whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimCondition {
                /**
                 *  @notice The criteria that make up a claim condition.
                 *
                 *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                 *                                        The same claim condition applies until the `startTimestamp`
                 *                                        of the next claim condition.
                 *
                 *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                 *                                        the claim condition.
                 *
                 *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                 *                                        under the claim condition.
                 *
                 *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                 *
                 *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                 *                                        condition.
                 *
                 *  @param pricePerToken                  The price required to pay per token claimed.
                 *
                 *  @param currency                       The currency in which the `pricePerToken` must be paid.
                 *
                 *  @param metadata                       Claim condition metadata.
                 */
                struct ClaimCondition {
                    uint256 startTimestamp;
                    uint256 maxClaimableSupply;
                    uint256 supplyClaimed;
                    uint256 quantityLimitPerWallet;
                    bytes32 merkleRoot;
                    uint256 pricePerToken;
                    address currency;
                    string metadata;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IClaimCondition.sol";
            /**
             *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimConditionMultiPhase is IClaimCondition {
                /**
                 *  @notice The set of all claim conditions, at any given moment.
                 *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                 *
                 *  @param currentStartId           The uid for the first claim condition amongst the current set of
                 *                                  claim conditions. The uid for each next claim condition is one
                 *                                  more than the previous claim condition's uid.
                 *
                 *  @param count                    The total number of phases / claim conditions in the list
                 *                                  of claim conditions.
                 *
                 *  @param conditions                   The claim conditions at a given uid. Claim conditions
                 *                                  are ordered in an ascending order by their `startTimestamp`.
                 *
                 *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                 */
                struct ClaimConditionList {
                    uint256 currentStartId;
                    uint256 count;
                    mapping(uint256 => ClaimCondition) conditions;
                    mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *  for you contract.
             *
             *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            interface IContractMetadata {
                /// @dev Returns the metadata URI of the contract.
                function contractURI() external view returns (string memory);
                /**
                 *  @dev Sets contract URI for the storefront-level metadata of the contract.
                 *       Only module admin can call this function.
                 */
                function setContractURI(string calldata _uri) external;
                /// @dev Emitted when the contract URI is updated.
                event ContractURIUpdated(string prevURI, string newURI);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IClaimConditionMultiPhase.sol";
            /**
             *  The interface `IDrop1155` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IDrop1155 is IClaimConditionMultiPhase {
                /**
                 *  @param proof Prood of concerned wallet's inclusion in an allowlist.
                 *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                 *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                 *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                 */
                struct AllowlistProof {
                    bytes32[] proof;
                    uint256 quantityLimitPerWallet;
                    uint256 pricePerToken;
                    address currency;
                }
                /// @notice Emitted when tokens are claimed.
                event TokensClaimed(
                    uint256 indexed claimConditionIndex,
                    address indexed claimer,
                    address indexed receiver,
                    uint256 tokenId,
                    uint256 quantityClaimed
                );
                /// @notice Emitted when the contract's claim conditions are updated.
                event ClaimConditionsUpdated(uint256 indexed tokenId, ClaimCondition[] claimConditions, bool resetEligibility);
                /**
                 *  @notice Lets an account claim a given quantity of NFTs.
                 *
                 *  @param receiver                       The receiver of the NFTs to claim.
                 *  @param tokenId                        The tokenId of the NFT to claim.
                 *  @param quantity                       The quantity of NFTs to claim.
                 *  @param currency                       The currency in which to pay for the claim.
                 *  @param pricePerToken                  The price per token to pay for the claim.
                 *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                 *                                        of the claim conditions that apply.
                 *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                 */
                function claim(
                    address receiver,
                    uint256 tokenId,
                    uint256 quantity,
                    address currency,
                    uint256 pricePerToken,
                    AllowlistProof calldata allowlistProof,
                    bytes memory data
                ) external payable;
                /**
                 *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                 *
                 *  @param tokenId                  The token ID for which to set mint conditions.
                 *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                 *
                 *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                 *                                  in the new claim conditions being set.
                 *
                 */
                function setClaimConditions(
                    uint256 tokenId,
                    ClaimCondition[] calldata phases,
                    bool resetClaimEligibility
                ) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            interface ILazyMint {
                /// @dev Emitted when tokens are lazy minted.
                event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                /**
                 *  @notice Lazy mints a given amount of NFTs.
                 *
                 *  @param amount           The number of NFTs to lazy mint.
                 *
                 *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *
                 *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                 *
                 *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 amount,
                    string calldata baseURIForTokens,
                    bytes calldata extraData
                ) external returns (uint256 batchId);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            interface IOperatorFilterRegistry {
                function isOperatorAllowed(address registrant, address operator) external view returns (bool);
                function register(address registrant) external;
                function registerAndSubscribe(address registrant, address subscription) external;
                function registerAndCopyEntries(address registrant, address registrantToCopy) external;
                function unregister(address addr) external;
                function updateOperator(
                    address registrant,
                    address operator,
                    bool filtered
                ) external;
                function updateOperators(
                    address registrant,
                    address[] calldata operators,
                    bool filtered
                ) external;
                function updateCodeHash(
                    address registrant,
                    bytes32 codehash,
                    bool filtered
                ) external;
                function updateCodeHashes(
                    address registrant,
                    bytes32[] calldata codeHashes,
                    bool filtered
                ) external;
                function subscribe(address registrant, address registrantToSubscribe) external;
                function unsubscribe(address registrant, bool copyExistingEntries) external;
                function subscriptionOf(address addr) external returns (address registrant);
                function subscribers(address registrant) external returns (address[] memory);
                function subscriberAt(address registrant, uint256 index) external returns (address);
                function copyEntriesOf(address registrant, address registrantToCopy) external;
                function isOperatorFiltered(address registrant, address operator) external returns (bool);
                function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
                function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
                function filteredOperators(address addr) external returns (address[] memory);
                function filteredCodeHashes(address addr) external returns (bytes32[] memory);
                function filteredOperatorAt(address registrant, uint256 index) external returns (address);
                function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
                function isRegistered(address addr) external returns (bool);
                function codeHashOf(address addr) external returns (bytes32);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            interface IOperatorFilterToggle {
                event OperatorRestriction(bool restriction);
                function operatorRestriction() external view returns (bool);
                function setOperatorRestriction(bool restriction) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *  information about who the contract's owner is.
             */
            interface IOwnable {
                /// @dev Returns the owner of the contract.
                function owner() external view returns (address);
                /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                function setOwner(address _newOwner) external;
                /// @dev Emitted when a new Owner is set.
                event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             * @dev External interface of AccessControl declared to support ERC165 detection.
             */
            interface IPermissions {
                /**
                 * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                 *
                 * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                 * {RoleAdminChanged} not being emitted signaling this.
                 *
                 * _Available since v3.1._
                 */
                event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                /**
                 * @dev Emitted when `account` is granted `role`.
                 *
                 * `sender` is the account that originated the contract call, an admin role
                 * bearer except when using {AccessControl-_setupRole}.
                 */
                event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Emitted when `account` is revoked `role`.
                 *
                 * `sender` is the account that originated the contract call:
                 *   - if using `revokeRole`, it is the admin role bearer
                 *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                 */
                event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Returns `true` if `account` has been granted `role`.
                 */
                function hasRole(bytes32 role, address account) external view returns (bool);
                /**
                 * @dev Returns the admin role that controls `role`. See {grantRole} and
                 * {revokeRole}.
                 *
                 * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                 */
                function getRoleAdmin(bytes32 role) external view returns (bytes32);
                /**
                 * @dev Grants `role` to `account`.
                 *
                 * If `account` had not been already granted `role`, emits a {RoleGranted}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function grantRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from `account`.
                 *
                 * If `account` had been granted `role`, emits a {RoleRevoked} event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function revokeRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from the calling account.
                 *
                 * Roles are often managed via {grantRole} and {revokeRole}: this function's
                 * purpose is to provide a mechanism for accounts to lose their privileges
                 * if they are compromised (such as when a trusted device is misplaced).
                 *
                 * If the calling account had been granted `role`, emits a {RoleRevoked}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must be `account`.
                 */
                function renounceRole(bytes32 role, address account) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IPermissions.sol";
            /**
             * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
             */
            interface IPermissionsEnumerable is IPermissions {
                /**
                 * @dev Returns one of the accounts that have `role`. `index` must be a
                 * value between 0 and {getRoleMemberCount}, non-inclusive.
                 *
                 * Role bearers are not sorted in any particular way, and their ordering may
                 * change at any point.
                 *
                 * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                 * you perform all queries on the same block. See the following
                 * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                 * for more information.
                 */
                function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                /**
                 * @dev Returns the number of accounts that have `role`. Can be used
                 * together with {getRoleMember} to enumerate all bearers of a role.
                 */
                function getRoleMemberCount(bytes32 role) external view returns (uint256);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about platform fees, if desired.
             */
            interface IPlatformFee {
                /// @dev Returns the platform fee bps and recipient.
                function getPlatformFeeInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the fees on primary sales.
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                /// @dev Emitted when fee on primary sales is updated.
                event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *  primary sales, if desired.
             */
            interface IPrimarySale {
                /// @dev The adress that receives all primary sales value.
                function primarySaleRecipient() external view returns (address);
                /// @dev Lets a module admin set the default recipient of all primary sales.
                function setPrimarySaleRecipient(address _saleRecipient) external;
                /// @dev Emitted when a new sale recipient is set.
                event PrimarySaleRecipientUpdated(address indexed recipient);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "../../eip/interface/IERC2981.sol";
            /**
             *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about royalty fees, if desired.
             *
             *  The `Royalty` contract is ERC2981 compliant.
             */
            interface IRoyalty is IERC2981 {
                struct RoyaltyInfo {
                    address recipient;
                    uint256 bps;
                }
                /// @dev Returns the royalty recipient and fee bps.
                function getDefaultRoyaltyInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the royalty bps and recipient.
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                function setRoyaltyInfoForToken(
                    uint256 tokenId,
                    address recipient,
                    uint256 bps
                ) external;
                /// @dev Returns the royalty recipient for a particular token Id.
                function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                /// @dev Emitted when royalty info is updated.
                event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                /// @dev Emitted when royalty recipient for tokenId is set
                event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            interface IWETH {
                function deposit() external payable;
                function withdraw(uint256 amount) external;
                function transfer(address to, uint256 value) external returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            // Helper interfaces
            import { IWETH } from "../interfaces/IWETH.sol";
            import "../openzeppelin-presets/token/ERC20/utils/SafeERC20.sol";
            library CurrencyTransferLib {
                using SafeERC20 for IERC20;
                /// @dev The address interpreted as native token of the chain.
                address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /// @dev Transfers a given amount of currency.
                function transferCurrency(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount
                ) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        safeTransferNativeToken(_to, _amount);
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfers a given amount of currency. (With native token wrapping)
                function transferCurrencyWithWrapper(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount,
                    address _nativeTokenWrapper
                ) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        if (_from == address(this)) {
                            // withdraw from weth then transfer withdrawn native token to recipient
                            IWETH(_nativeTokenWrapper).withdraw(_amount);
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        } else if (_to == address(this)) {
                            // store native currency in weth
                            require(_amount == msg.value, "msg.value != amount");
                            IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                        } else {
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        }
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                function safeTransferERC20(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount
                ) internal {
                    if (_from == _to) {
                        return;
                    }
                    if (_from == address(this)) {
                        IERC20(_currency).safeTransfer(_to, _amount);
                    } else {
                        IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                    }
                }
                /// @dev Transfers `amount` of native token to `to`.
                function safeTransferNativeToken(address to, uint256 value) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    require(success, "native token transfer failed");
                }
                /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                function safeTransferNativeTokenWithWrapper(
                    address to,
                    uint256 value,
                    address _nativeTokenWrapper
                ) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    if (!success) {
                        IWETH(_nativeTokenWrapper).deposit{ value: value }();
                        IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/utils/cryptography/MerkleProof.sol
            // Copied from https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
            pragma solidity ^0.8.0;
            /**
             * @dev These functions deal with verification of Merkle Trees proofs.
             *
             * The proofs can be generated using the JavaScript library
             * https://github.com/miguelmota/merkletreejs[merkletreejs].
             * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
             *
             * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
             *
             * Source: https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
             */
            library MerkleProof {
                /**
                 * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                 * defined by `root`. For this, a `proof` must be provided, containing
                 * sibling hashes on the branch from the leaf to the root of the tree. Each
                 * pair of leaves and each pair of pre-images are assumed to be sorted.
                 */
                function verify(
                    bytes32[] memory proof,
                    bytes32 root,
                    bytes32 leaf
                ) internal pure returns (bool, uint256) {
                    bytes32 computedHash = leaf;
                    uint256 index = 0;
                    for (uint256 i = 0; i < proof.length; i++) {
                        index *= 2;
                        bytes32 proofElement = proof[i];
                        if (computedHash <= proofElement) {
                            // Hash(current computed hash + current element of the proof)
                            computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                        } else {
                            // Hash(current element of the proof + current computed hash)
                            computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                            index += 1;
                        }
                    }
                    // Check if the computed hash (root) is equal to the provided root
                    return (computedHash == root, index);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library TWAddress {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{ value: value }(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev String operations.
             */
            library TWStrings {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    // Inspired by OraclizeAPI's implementation - MIT licence
                    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                    if (value == 0) {
                        return "0";
                    }
                    uint256 temp = value;
                    uint256 digits;
                    while (temp != 0) {
                        digits++;
                        temp /= 10;
                    }
                    bytes memory buffer = new bytes(digits);
                    while (value != 0) {
                        digits -= 1;
                        buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                        value /= 10;
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    if (value == 0) {
                        return "0x00";
                    }
                    uint256 temp = value;
                    uint256 length = 0;
                    while (temp != 0) {
                        length++;
                        temp >>= 8;
                    }
                    return toHexString(value, length);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    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_SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    return string(buffer);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.0 (metatx/ERC2771Context.sol)
            pragma solidity ^0.8.11;
            import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
            /**
             * @dev Context variant with ERC2771 support.
             */
            abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                mapping(address => bool) private _trustedForwarder;
                function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                    __Context_init_unchained();
                    __ERC2771Context_init_unchained(trustedForwarder);
                }
                function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                    for (uint256 i = 0; i < trustedForwarder.length; i++) {
                        _trustedForwarder[trustedForwarder[i]] = true;
                    }
                }
                function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                    return _trustedForwarder[forwarder];
                }
                function _msgSender() internal view virtual override returns (address sender) {
                    if (isTrustedForwarder(msg.sender)) {
                        // The assembly code is more direct than the Solidity version using `abi.decode`.
                        assembly {
                            sender := shr(96, calldataload(sub(calldatasize(), 20)))
                        }
                    } else {
                        return super._msgSender();
                    }
                }
                function _msgData() internal view virtual override returns (bytes calldata) {
                    if (isTrustedForwarder(msg.sender)) {
                        return msg.data[:msg.data.length - 20];
                    } else {
                        return super._msgData();
                    }
                }
                uint256[49] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
            pragma solidity ^0.8.0;
            import "../../../../eip/interface/IERC20.sol";
            import "../../../../lib/TWAddress.sol";
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using TWAddress for address;
                function safeTransfer(
                    IERC20 token,
                    address to,
                    uint256 value
                ) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
                function safeTransferFrom(
                    IERC20 token,
                    address from,
                    address to,
                    uint256 value
                ) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    require(
                        (value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
                function safeIncreaseAllowance(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    uint256 newAllowance = token.allowance(address(this), spender) + value;
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
                function safeDecreaseAllowance(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    unchecked {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                        uint256 newAllowance = oldAllowance - value;
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                    }
                }
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) {
                        // Return data is optional
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
            pragma solidity ^0.8.0;
            import "../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
            pragma solidity ^0.8.2;
            import "../../utils/AddressUpgradeable.sol";
            /**
             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
             *
             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
             * case an upgrade adds a module that needs to be initialized.
             *
             * For example:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * contract MyToken is ERC20Upgradeable {
             *     function initialize() initializer public {
             *         __ERC20_init("MyToken", "MTK");
             *     }
             * }
             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
             *     function initializeV2() reinitializer(2) public {
             *         __ERC20Permit_init("MyToken");
             *     }
             * }
             * ```
             *
             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
             *
             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
             *
             * [CAUTION]
             * ====
             * Avoid leaving a contract uninitialized.
             *
             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * /// @custom:oz-upgrades-unsafe-allow constructor
             * constructor() {
             *     _disableInitializers();
             * }
             * ```
             * ====
             */
            abstract contract Initializable {
                /**
                 * @dev Indicates that the contract has been initialized.
                 * @custom:oz-retyped-from bool
                 */
                uint8 private _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool private _initializing;
                /**
                 * @dev Triggered when the contract has been initialized or reinitialized.
                 */
                event Initialized(uint8 version);
                /**
                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                 * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                 */
                modifier initializer() {
                    bool isTopLevelCall = !_initializing;
                    require(
                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                        "Initializable: contract is already initialized"
                    );
                    _initialized = 1;
                    if (isTopLevelCall) {
                        _initializing = true;
                    }
                    _;
                    if (isTopLevelCall) {
                        _initializing = false;
                        emit Initialized(1);
                    }
                }
                /**
                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                 * used to initialize parent contracts.
                 *
                 * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                 * initialization step. This is essential to configure modules that are added through upgrades and that require
                 * initialization.
                 *
                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                 * a contract, executing them in the right order is up to the developer or operator.
                 */
                modifier reinitializer(uint8 version) {
                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                    _initialized = version;
                    _initializing = true;
                    _;
                    _initializing = false;
                    emit Initialized(version);
                }
                /**
                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                 */
                modifier onlyInitializing() {
                    require(_initializing, "Initializable: contract is not initializing");
                    _;
                }
                /**
                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                 * through proxies.
                 */
                function _disableInitializers() internal virtual {
                    require(!_initializing, "Initializable: contract is initializing");
                    if (_initialized < type(uint8).max) {
                        _initialized = type(uint8).max;
                        emit Initialized(type(uint8).max);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/ERC1155.sol)
            pragma solidity ^0.8.0;
            import "./IERC1155Upgradeable.sol";
            import "./IERC1155ReceiverUpgradeable.sol";
            import "./extensions/IERC1155MetadataURIUpgradeable.sol";
            import "../../utils/AddressUpgradeable.sol";
            import "../../utils/ContextUpgradeable.sol";
            import "../../utils/introspection/ERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the basic standard multi-token.
             * See https://eips.ethereum.org/EIPS/eip-1155
             * Originally based on code by Enjin: https://github.com/enjin/erc-1155
             *
             * _Available since v3.1._
             */
            contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
                using AddressUpgradeable for address;
                // Mapping from token ID to account balances
                mapping(uint256 => mapping(address => uint256)) private _balances;
                // Mapping from account to operator approvals
                mapping(address => mapping(address => bool)) private _operatorApprovals;
                // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
                string private _uri;
                /**
                 * @dev See {_setURI}.
                 */
                function __ERC1155_init(string memory uri_) internal onlyInitializing {
                    __ERC1155_init_unchained(uri_);
                }
                function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
                    _setURI(uri_);
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                    return
                        interfaceId == type(IERC1155Upgradeable).interfaceId ||
                        interfaceId == type(IERC1155MetadataURIUpgradeable).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) public view virtual override returns (string memory) {
                    return _uri;
                }
                /**
                 * @dev See {IERC1155-balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
                    require(account != address(0), "ERC1155: address zero is not a valid owner");
                    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
                    override
                    returns (uint256[] memory)
                {
                    require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
                    uint256[] memory batchBalances = new uint256[](accounts.length);
                    for (uint256 i = 0; i < accounts.length; ++i) {
                        batchBalances[i] = balanceOf(accounts[i], ids[i]);
                    }
                    return batchBalances;
                }
                /**
                 * @dev See {IERC1155-setApprovalForAll}.
                 */
                function setApprovalForAll(address operator, bool approved) public virtual override {
                    _setApprovalForAll(_msgSender(), operator, approved);
                }
                /**
                 * @dev See {IERC1155-isApprovedForAll}.
                 */
                function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
                    return _operatorApprovals[account][operator];
                }
                /**
                 * @dev See {IERC1155-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner nor approved"
                    );
                    _safeTransferFrom(from, to, id, amount, data);
                }
                /**
                 * @dev See {IERC1155-safeBatchTransferFrom}.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner nor approved"
                    );
                    _safeBatchTransferFrom(from, to, ids, amounts, data);
                }
                /**
                 * @dev Transfers `amount` 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 `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 amount,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, from, to, id, amount);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, 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.
                 */
                function _safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; ++i) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                        _balances[id][to] += amount;
                    }
                    emit TransferBatch(operator, from, to, ids, amounts);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
                }
                /**
                 * @dev Sets a new URI for all token types, by relying on the token type ID
                 * substitution mechanism
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
                 *
                 * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
                 * URI or any of the amounts in the JSON file at said URI will be replaced by
                 * clients with the token type ID.
                 *
                 * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
                 * interpreted by clients as
                 * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
                 * for token type ID 0x4cce0.
                 *
                 * See {uri}.
                 *
                 * Because these URIs cannot be meaningfully represented by the {URI} event,
                 * this function emits no events.
                 */
                function _setURI(string memory newuri) internal virtual {
                    _uri = newuri;
                }
                /**
                 * @dev Creates `amount` tokens of token 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 amount,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, address(0), to, id, amount);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function _mintBatch(
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; i++) {
                        _balances[ids[i]][to] += amounts[i];
                    }
                    emit TransferBatch(operator, address(0), to, ids, amounts);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
                }
                /**
                 * @dev Destroys `amount` tokens of token type `id` from `from`
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `from` must have at least `amount` tokens of token type `id`.
                 */
                function _burn(
                    address from,
                    uint256 id,
                    uint256 amount
                ) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    emit TransferSingle(operator, from, address(0), id, amount);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 */
                function _burnBatch(
                    address from,
                    uint256[] memory ids,
                    uint256[] memory amounts
                ) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    for (uint256 i = 0; i < ids.length; i++) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                    }
                    emit TransferBatch(operator, from, address(0), ids, amounts);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev Approve `operator` to operate on all of `owner` tokens
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function _setApprovalForAll(
                    address owner,
                    address operator,
                    bool approved
                ) internal virtual {
                    require(owner != operator, "ERC1155: setting approval status for self");
                    _operatorApprovals[owner][operator] = approved;
                    emit ApprovalForAll(owner, operator, approved);
                }
                /**
                 * @dev Hook that is called before any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `ids` and `amounts` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                /**
                 * @dev Hook that is called after any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `id` and `amount` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _afterTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                function _doSafeTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non ERC1155Receiver implementer");
                        }
                    }
                }
                function _doSafeBatchTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                            bytes4 response
                        ) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non ERC1155Receiver implementer");
                        }
                    }
                }
                function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                    uint256[] memory array = new uint256[](1);
                    array[0] = element;
                    return array;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[47] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
                /**
                 * @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 v4.7.0) (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Emitted when `value` tokens of token 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 amount 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 `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * 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 `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 amount,
                    bytes calldata data
                ) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` 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 amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Upgradeable.sol";
            /**
             * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
             * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
                /**
                 * @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 v4.7.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library AddressUpgradeable {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            import "../proxy/utils/Initializable.sol";
            /**
             * @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 ContextUpgradeable is Initializable {
                function __Context_init() internal onlyInitializing {
                }
                function __Context_init_unchained() internal onlyInitializing {
                }
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
            pragma solidity ^0.8.0;
            import "./AddressUpgradeable.sol";
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Provides a function to batch together multiple calls in a single external call.
             *
             * _Available since v4.1._
             */
            abstract contract MulticallUpgradeable is Initializable {
                function __Multicall_init() internal onlyInitializing {
                }
                function __Multicall_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev Receives and executes a batch of function calls on this contract.
                 */
                function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                    results = new bytes[](data.length);
                    for (uint256 i = 0; i < data.length; i++) {
                        results[i] = _functionDelegateCall(address(this), data[i]);
                    }
                    return results;
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                    require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev String operations.
             */
            library StringsUpgradeable {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                uint8 private constant _ADDRESS_LENGTH = 20;
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    // Inspired by OraclizeAPI's implementation - MIT licence
                    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                    if (value == 0) {
                        return "0";
                    }
                    uint256 temp = value;
                    uint256 digits;
                    while (temp != 0) {
                        digits++;
                        temp /= 10;
                    }
                    bytes memory buffer = new bytes(digits);
                    while (value != 0) {
                        digits -= 1;
                        buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                        value /= 10;
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    if (value == 0) {
                        return "0x00";
                    }
                    uint256 temp = value;
                    uint256 length = 0;
                    while (temp != 0) {
                        length++;
                        temp >>= 8;
                    }
                    return toHexString(value, length);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    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_SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    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);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                function __ERC165_init() internal onlyInitializing {
                }
                function __ERC165_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165Upgradeable).interfaceId;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165Upgradeable {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            

            File 4 of 4: DropERC1155
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.11;
            //  ==========  External imports    ==========
            import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
            //  ==========  Internal imports    ==========
            import "../openzeppelin-presets/metatx/ERC2771ContextUpgradeable.sol";
            import "../lib/CurrencyTransferLib.sol";
            //  ==========  Features    ==========
            import "../extension/ContractMetadata.sol";
            import "../extension/PlatformFee.sol";
            import "../extension/Royalty.sol";
            import "../extension/PrimarySale.sol";
            import "../extension/Ownable.sol";
            import "../extension/LazyMint.sol";
            import "../extension/PermissionsEnumerable.sol";
            import "../extension/Drop1155.sol";
            // OpenSea operator filter
            import "../extension/DefaultOperatorFiltererUpgradeable.sol";
            contract DropERC1155 is
                Initializable,
                ContractMetadata,
                PlatformFee,
                Royalty,
                PrimarySale,
                Ownable,
                LazyMint,
                PermissionsEnumerable,
                Drop1155,
                ERC2771ContextUpgradeable,
                MulticallUpgradeable,
                DefaultOperatorFiltererUpgradeable,
                ERC1155Upgradeable
            {
                using StringsUpgradeable for uint256;
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                // Token name
                string public name;
                // Token symbol
                string public symbol;
                /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                bytes32 private transferRole;
                /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                bytes32 private minterRole;
                /// @dev Max bps in the thirdweb system.
                uint256 private constant MAX_BPS = 10_000;
                /*///////////////////////////////////////////////////////////////
                                            Mappings
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public totalSupply;
                /// @dev Mapping from token ID => maximum possible total circulating supply of tokens with that ID.
                mapping(uint256 => uint256) public maxTotalSupply;
                /// @dev Mapping from token ID => the address of the recipient of primary sales.
                mapping(uint256 => address) public saleRecipient;
                /*///////////////////////////////////////////////////////////////
                                           Events
                //////////////////////////////////////////////////////////////*/
                /// @dev Emitted when the global max supply of a token is updated.
                event MaxTotalSupplyUpdated(uint256 tokenId, uint256 maxTotalSupply);
                /// @dev Emitted when the sale recipient for a particular tokenId is updated.
                event SaleRecipientForTokenUpdated(uint256 indexed tokenId, address saleRecipient);
                /*///////////////////////////////////////////////////////////////
                                Constructor + initializer logic
                //////////////////////////////////////////////////////////////*/
                constructor() initializer {}
                /// @dev Initiliazes the contract, like a constructor.
                function initialize(
                    address _defaultAdmin,
                    string memory _name,
                    string memory _symbol,
                    string memory _contractURI,
                    address[] memory _trustedForwarders,
                    address _saleRecipient,
                    address _royaltyRecipient,
                    uint128 _royaltyBps,
                    uint128 _platformFeeBps,
                    address _platformFeeRecipient
                ) external initializer {
                    bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                    bytes32 _minterRole = keccak256("MINTER_ROLE");
                    // Initialize inherited contracts, most base-like -> most derived.
                    __ERC2771Context_init(_trustedForwarders);
                    __ERC1155_init_unchained("");
                    __DefaultOperatorFilterer_init();
                    // Initialize this contract's state.
                    _setupContractURI(_contractURI);
                    _setupOwner(_defaultAdmin);
                    _setOperatorRestriction(true);
                    _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                    _setupRole(_minterRole, _defaultAdmin);
                    _setupRole(_transferRole, _defaultAdmin);
                    _setupRole(_transferRole, address(0));
                    _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                    _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                    _setupPrimarySaleRecipient(_saleRecipient);
                    transferRole = _transferRole;
                    minterRole = _minterRole;
                    name = _name;
                    symbol = _symbol;
                }
                /*///////////////////////////////////////////////////////////////
                                    ERC 165 / 1155 / 2981 logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Returns the uri for a given tokenId.
                function uri(uint256 _tokenId) public view override returns (string memory) {
                    string memory batchUri = _getBaseURI(_tokenId);
                    return string(abi.encodePacked(batchUri, _tokenId.toString()));
                }
                /// @dev See ERC 165
                function supportsInterface(bytes4 interfaceId)
                    public
                    view
                    virtual
                    override(ERC1155Upgradeable, IERC165)
                    returns (bool)
                {
                    return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                }
                /*///////////////////////////////////////////////////////////////
                                    Contract identifiers
                //////////////////////////////////////////////////////////////*/
                function contractType() external pure returns (bytes32) {
                    return bytes32("DropERC1155");
                }
                function contractVersion() external pure returns (uint8) {
                    return uint8(4);
                }
                /*///////////////////////////////////////////////////////////////
                                    Setter functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets a module admin set a max total supply for token.
                function setMaxTotalSupply(uint256 _tokenId, uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                    maxTotalSupply[_tokenId] = _maxTotalSupply;
                    emit MaxTotalSupplyUpdated(_tokenId, _maxTotalSupply);
                }
                /// @dev Lets a contract admin set the recipient for all primary sales.
                function setSaleRecipientForToken(uint256 _tokenId, address _saleRecipient) external onlyRole(DEFAULT_ADMIN_ROLE) {
                    saleRecipient[_tokenId] = _saleRecipient;
                    emit SaleRecipientForTokenUpdated(_tokenId, _saleRecipient);
                }
                /*///////////////////////////////////////////////////////////////
                                    Internal functions
                //////////////////////////////////////////////////////////////*/
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address,
                    uint256 _quantity,
                    address,
                    uint256,
                    AllowlistProof calldata,
                    bytes memory
                ) internal view override {
                    require(
                        maxTotalSupply[_tokenId] == 0 || totalSupply[_tokenId] + _quantity <= maxTotalSupply[_tokenId],
                        "exceed max total supply"
                    );
                }
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256 _tokenId,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal override {
                    if (_pricePerToken == 0) {
                        return;
                    }
                    (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                    address _saleRecipient = _primarySaleRecipient == address(0)
                        ? (saleRecipient[_tokenId] == address(0) ? primarySaleRecipient() : saleRecipient[_tokenId])
                        : _primarySaleRecipient;
                    uint256 totalPrice = _quantityToClaim * _pricePerToken;
                    uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                    if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                        if (msg.value != totalPrice) {
                            revert("!Price");
                        }
                    }
                    CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                    CurrencyTransferLib.transferCurrency(_currency, _msgSender(), _saleRecipient, totalPrice - platformFees);
                }
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(
                    address _to,
                    uint256 _tokenId,
                    uint256 _quantityBeingClaimed
                ) internal override {
                    _mint(_to, _tokenId, _quantityBeingClaimed, "");
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether owner can be set in the given execution context.
                function _canSetOwner() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Checks whether platform fee info can be set in the given execution context.
                function _canSetClaimConditions() internal view override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /// @dev Returns whether lazy minting can be done in the given execution context.
                function _canLazyMint() internal view virtual override returns (bool) {
                    return hasRole(minterRole, _msgSender());
                }
                /// @dev Returns whether operator restriction can be set in the given execution context.
                function _canSetOperatorRestriction() internal virtual override returns (bool) {
                    return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                }
                /*///////////////////////////////////////////////////////////////
                                    Miscellaneous
                //////////////////////////////////////////////////////////////*/
                /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                function nextTokenIdToMint() external view returns (uint256) {
                    return nextTokenIdToLazyMint;
                }
                /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good)
                function burnBatch(
                    address account,
                    uint256[] memory ids,
                    uint256[] memory values
                ) public virtual {
                    require(
                        account == _msgSender() || isApprovedForAll(account, _msgSender()),
                        "ERC1155: caller is not owner nor approved."
                    );
                    _burnBatch(account, ids, values);
                }
                /**
                 * @dev See {ERC1155-_beforeTokenTransfer}.
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual override {
                    super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    // if transfer is restricted on the contract, we still want to allow burning and minting
                    if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                        require(hasRole(transferRole, from) || hasRole(transferRole, to), "restricted to TRANSFER_ROLE holders.");
                    }
                    if (from == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] += amounts[i];
                        }
                    }
                    if (to == address(0)) {
                        for (uint256 i = 0; i < ids.length; ++i) {
                            totalSupply[ids[i]] -= amounts[i];
                        }
                    }
                }
                /// @dev See {ERC1155-setApprovalForAll}
                function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
                    super.setApprovalForAll(operator, approved);
                }
                /**
                 * @dev See {IERC1155-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) public override(ERC1155Upgradeable) onlyAllowedOperator(from) {
                    super.safeTransferFrom(from, to, id, amount, data);
                }
                /**
                 * @dev See {IERC1155-safeBatchTransferFrom}.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) public override(ERC1155Upgradeable) onlyAllowedOperator(from) {
                    super.safeBatchTransferFrom(from, to, ids, amounts, data);
                }
                function _dropMsgSender() internal view virtual override returns (address) {
                    return _msgSender();
                }
                function _msgSender()
                    internal
                    view
                    virtual
                    override(ContextUpgradeable, ERC2771ContextUpgradeable)
                    returns (address sender)
                {
                    return ERC2771ContextUpgradeable._msgSender();
                }
                function _msgData()
                    internal
                    view
                    virtual
                    override(ContextUpgradeable, ERC2771ContextUpgradeable)
                    returns (bytes calldata)
                {
                    return ERC2771ContextUpgradeable._msgData();
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * [EIP](https://eips.ethereum.org/EIPS/eip-165).
             *
             * 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
                 * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             * @title ERC20 interface
             * @dev see https://github.com/ethereum/EIPs/issues/20
             */
            interface IERC20 {
                function totalSupply() external view returns (uint256);
                function balanceOf(address who) external view returns (uint256);
                function allowance(address owner, address spender) external view returns (uint256);
                function transfer(address to, uint256 value) external returns (bool);
                function approve(address spender, uint256 value) external returns (bool);
                function transferFrom(
                    address from,
                    address to,
                    uint256 value
                ) external returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
                event Approval(address indexed owner, address indexed spender, uint256 value);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981 is IERC165 {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  @title   Batch-mint Metadata
             *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
             *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
             *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
             */
            contract BatchMintMetadata {
                /// @dev Largest tokenId of each batch of tokens with the same baseURI.
                uint256[] private batchIds;
                /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                mapping(uint256 => string) private baseURI;
                /**
                 *  @notice         Returns the count of batches of NFTs.
                 *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                 *                  See {batchIds}.
                 */
                function getBaseURICount() public view returns (uint256) {
                    return batchIds.length;
                }
                /**
                 *  @notice         Returns the ID for the batch of tokens the given tokenId belongs to.
                 *  @dev            See {getBaseURICount}.
                 *  @param _index   ID of a token.
                 */
                function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                    if (_index >= getBaseURICount()) {
                        revert("Invalid index");
                    }
                    return batchIds[_index];
                }
                /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            index = i;
                            batchId = indices[i];
                            return (batchId, index);
                        }
                    }
                    revert("Invalid tokenId");
                }
                /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                    uint256 numOfTokenBatches = getBaseURICount();
                    uint256[] memory indices = batchIds;
                    for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                        if (_tokenId < indices[i]) {
                            return baseURI[indices[i]];
                        }
                    }
                    revert("Invalid tokenId");
                }
                /// @dev Sets the base URI for the batch of tokens with the given batchId.
                function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                    baseURI[_batchId] = _baseURI;
                }
                /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                function _batchMintMetadata(
                    uint256 _startId,
                    uint256 _amountToMint,
                    string memory _baseURIForTokens
                ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                    batchId = _startId + _amountToMint;
                    nextTokenIdToMint = batchId;
                    batchIds.push(batchId);
                    baseURI[batchId] = _baseURIForTokens;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IContractMetadata.sol";
            /**
             *  @title   Contract Metadata
             *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *           for you contract.
             *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            abstract contract ContractMetadata is IContractMetadata {
                /// @notice Returns the contract metadata URI.
                string public override contractURI;
                /**
                 *  @notice         Lets a contract admin set the URI for contract-level metadata.
                 *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                 *                  See {_canSetContractURI}.
                 *                  Emits {ContractURIUpdated Event}.
                 *
                 *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function setContractURI(string memory _uri) external override {
                    if (!_canSetContractURI()) {
                        revert("Not authorized");
                    }
                    _setupContractURI(_uri);
                }
                /// @dev Lets a contract admin set the URI for contract-level metadata.
                function _setupContractURI(string memory _uri) internal {
                    string memory prevURI = contractURI;
                    contractURI = _uri;
                    emit ContractURIUpdated(prevURI, _uri);
                }
                /// @dev Returns whether contract metadata can be set in the given execution context.
                function _canSetContractURI() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import { OperatorFiltererUpgradeable } from "./OperatorFiltererUpgradeable.sol";
            abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                function __DefaultOperatorFilterer_init() internal {
                    OperatorFiltererUpgradeable.__OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IDrop1155.sol";
            import "../lib/MerkleProof.sol";
            abstract contract Drop1155 is IDrop1155 {
                /*///////////////////////////////////////////////////////////////
                                        State variables
                //////////////////////////////////////////////////////////////*/
                /// @dev Mapping from token ID => the set of all claim conditions, at any given moment, for tokens of the token ID.
                mapping(uint256 => ClaimConditionList) public claimCondition;
                /*///////////////////////////////////////////////////////////////
                                        Drop logic
                //////////////////////////////////////////////////////////////*/
                /// @dev Lets an account claim tokens.
                function claim(
                    address _receiver,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) public payable virtual override {
                    _beforeClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                    uint256 activeConditionId = getActiveClaimConditionId(_tokenId);
                    verifyClaim(
                        activeConditionId,
                        _dropMsgSender(),
                        _tokenId,
                        _quantity,
                        _currency,
                        _pricePerToken,
                        _allowlistProof
                    );
                    // Update contract state.
                    claimCondition[_tokenId].conditions[activeConditionId].supplyClaimed += _quantity;
                    claimCondition[_tokenId].supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                    // If there's a price, collect price.
                    collectPriceOnClaim(_tokenId, address(0), _quantity, _currency, _pricePerToken);
                    // Mint the relevant NFTs to claimer.
                    transferTokensOnClaim(_receiver, _tokenId, _quantity);
                    emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, _tokenId, _quantity);
                    _afterClaim(_tokenId, _receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                }
                /// @dev Lets a contract admin set claim conditions.
                function setClaimConditions(
                    uint256 _tokenId,
                    ClaimCondition[] calldata _conditions,
                    bool _resetClaimEligibility
                ) external virtual override {
                    if (!_canSetClaimConditions()) {
                        revert("Not authorized");
                    }
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    uint256 existingStartIndex = conditionList.currentStartId;
                    uint256 existingPhaseCount = conditionList.count;
                    /**
                     *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                     *  by `supplyClaimedByWallet`.
                     */
                    uint256 newStartIndex = existingStartIndex;
                    if (_resetClaimEligibility) {
                        newStartIndex = existingStartIndex + existingPhaseCount;
                    }
                    conditionList.count = _conditions.length;
                    conditionList.currentStartId = newStartIndex;
                    uint256 lastConditionStartTimestamp;
                    for (uint256 i = 0; i < _conditions.length; i++) {
                        require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                        uint256 supplyClaimedAlready = conditionList.conditions[newStartIndex + i].supplyClaimed;
                        if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                            revert("max supply claimed");
                        }
                        conditionList.conditions[newStartIndex + i] = _conditions[i];
                        conditionList.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                        lastConditionStartTimestamp = _conditions[i].startTimestamp;
                    }
                    /**
                     *  Gas refunds (as much as possible)
                     *
                     *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                     *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                     *
                     *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                     *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                     *  by the conditions in `_conditions`.
                     */
                    if (_resetClaimEligibility) {
                        for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                            delete conditionList.conditions[i];
                        }
                    } else {
                        if (existingPhaseCount > _conditions.length) {
                            for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                delete conditionList.conditions[newStartIndex + i];
                            }
                        }
                    }
                    emit ClaimConditionsUpdated(_tokenId, _conditions, _resetClaimEligibility);
                }
                /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                function verifyClaim(
                    uint256 _conditionId,
                    address _claimer,
                    uint256 _tokenId,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof
                ) public view returns (bool isOverride) {
                    ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].conditions[_conditionId];
                    uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                    uint256 claimPrice = currentClaimPhase.pricePerToken;
                    address claimCurrency = currentClaimPhase.currency;
                    if (currentClaimPhase.merkleRoot != bytes32(0)) {
                        (isOverride, ) = MerkleProof.verify(
                            _allowlistProof.proof,
                            currentClaimPhase.merkleRoot,
                            keccak256(
                                abi.encodePacked(
                                    _claimer,
                                    _allowlistProof.quantityLimitPerWallet,
                                    _allowlistProof.pricePerToken,
                                    _allowlistProof.currency
                                )
                            )
                        );
                    }
                    if (isOverride) {
                        claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                            ? _allowlistProof.quantityLimitPerWallet
                            : claimLimit;
                        claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                            ? _allowlistProof.pricePerToken
                            : claimPrice;
                        claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                            ? _allowlistProof.currency
                            : claimCurrency;
                    }
                    uint256 supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                    if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                        revert("!PriceOrCurrency");
                    }
                    if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                        revert("!Qty");
                    }
                    if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                        revert("!MaxSupply");
                    }
                    if (currentClaimPhase.startTimestamp > block.timestamp) {
                        revert("cant claim yet");
                    }
                }
                /// @dev At any given moment, returns the uid for the active claim condition.
                function getActiveClaimConditionId(uint256 _tokenId) public view returns (uint256) {
                    ClaimConditionList storage conditionList = claimCondition[_tokenId];
                    for (uint256 i = conditionList.currentStartId + conditionList.count; i > conditionList.currentStartId; i--) {
                        if (block.timestamp >= conditionList.conditions[i - 1].startTimestamp) {
                            return i - 1;
                        }
                    }
                    revert("!CONDITION.");
                }
                /// @dev Returns the claim condition at the given uid.
                function getClaimConditionById(uint256 _tokenId, uint256 _conditionId)
                    external
                    view
                    returns (ClaimCondition memory condition)
                {
                    condition = claimCondition[_tokenId].conditions[_conditionId];
                }
                /// @dev Returns the supply claimed by claimer for a given conditionId.
                function getSupplyClaimedByWallet(
                    uint256 _tokenId,
                    uint256 _conditionId,
                    address _claimer
                ) public view returns (uint256 supplyClaimedByWallet) {
                    supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer];
                }
                /*////////////////////////////////////////////////////////////////////
                    Optional hooks that can be implemented in the derived contract
                ///////////////////////////////////////////////////////////////////*/
                /// @dev Exposes the ability to override the msg sender.
                function _dropMsgSender() internal virtual returns (address) {
                    return msg.sender;
                }
                /// @dev Runs before every `claim` function call.
                function _beforeClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /// @dev Runs after every `claim` function call.
                function _afterClaim(
                    uint256 _tokenId,
                    address _receiver,
                    uint256 _quantity,
                    address _currency,
                    uint256 _pricePerToken,
                    AllowlistProof calldata _allowlistProof,
                    bytes memory _data
                ) internal virtual {}
                /*///////////////////////////////////////////////////////////////
                    Virtual functions: to be implemented in derived contract
                //////////////////////////////////////////////////////////////*/
                /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                function collectPriceOnClaim(
                    uint256 _tokenId,
                    address _primarySaleRecipient,
                    uint256 _quantityToClaim,
                    address _currency,
                    uint256 _pricePerToken
                ) internal virtual;
                /// @dev Transfers the NFTs being claimed.
                function transferTokensOnClaim(
                    address _to,
                    uint256 _tokenId,
                    uint256 _quantityBeingClaimed
                ) internal virtual;
                /// @dev Determine what wallet can update claim conditions
                function _canSetClaimConditions() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/ILazyMint.sol";
            import "./BatchMintMetadata.sol";
            /**
             *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                uint256 internal nextTokenIdToLazyMint;
                /**
                 *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                 *
                 *  @param _amount           The number of NFTs to lazy mint.
                 *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                 *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 _amount,
                    string calldata _baseURIForTokens,
                    bytes calldata _data
                ) public virtual override returns (uint256 batchId) {
                    if (!_canLazyMint()) {
                        revert("Not authorized");
                    }
                    if (_amount == 0) {
                        revert("0 amt");
                    }
                    uint256 startId = nextTokenIdToLazyMint;
                    (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                    emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                    return batchId;
                }
                /// @dev Returns whether lazy minting can be performed in the given execution context.
                function _canLazyMint() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            import "./interface/IOperatorFilterToggle.sol";
            abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                bool public operatorRestriction;
                function setOperatorRestriction(bool _restriction) external {
                    require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                    _setOperatorRestriction(_restriction);
                }
                function _setOperatorRestriction(bool _restriction) internal {
                    operatorRestriction = _restriction;
                    emit OperatorRestriction(_restriction);
                }
                function _canSetOperatorRestriction() internal virtual returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.12;
            import "./interface/IOperatorFilterRegistry.sol";
            import "./OperatorFilterToggle.sol";
            abstract contract OperatorFiltererUpgradeable is OperatorFilterToggle {
                error OperatorNotAllowed(address operator);
                IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                    IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) internal {
                    // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                    // will not revert, but the contract will need to be registered with the registry once it is deployed in
                    // order for the modifier to filter addresses.
                    if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                        if (!OPERATOR_FILTER_REGISTRY.isRegistered(address(this))) {
                            if (subscribe) {
                                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                            } else {
                                if (subscriptionOrRegistrantToCopy != address(0)) {
                                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                                } else {
                                    OPERATOR_FILTER_REGISTRY.register(address(this));
                                }
                            }
                        }
                    }
                }
                modifier onlyAllowedOperator(address from) virtual {
                    // Check registry code length to facilitate testing in environments without a deployed registry.
                    if (operatorRestriction) {
                        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                            // Allow spending tokens from addresses with balance
                            // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                            // from an EOA.
                            if (from == msg.sender) {
                                _;
                                return;
                            }
                            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), msg.sender)) {
                                revert OperatorNotAllowed(msg.sender);
                            }
                        }
                    }
                    _;
                }
                modifier onlyAllowedOperatorApproval(address operator) virtual {
                    // Check registry code length to facilitate testing in environments without a deployed registry.
                    if (operatorRestriction) {
                        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                                revert OperatorNotAllowed(operator);
                            }
                        }
                    }
                    _;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IOwnable.sol";
            /**
             *  @title   Ownable
             *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *           information about who the contract's owner is.
             */
            abstract contract Ownable is IOwnable {
                /// @dev Owner of the contract (purpose: OpenSea compatibility)
                address private _owner;
                /// @dev Reverts if caller is not the owner.
                modifier onlyOwner() {
                    if (msg.sender != _owner) {
                        revert("Not authorized");
                    }
                    _;
                }
                /**
                 *  @notice Returns the owner of the contract.
                 */
                function owner() public view override returns (address) {
                    return _owner;
                }
                /**
                 *  @notice Lets an authorized wallet set a new owner for the contract.
                 *  @param _newOwner The address to set as the new owner of the contract.
                 */
                function setOwner(address _newOwner) external override {
                    if (!_canSetOwner()) {
                        revert("Not authorized");
                    }
                    _setupOwner(_newOwner);
                }
                /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                function _setupOwner(address _newOwner) internal {
                    address _prevOwner = _owner;
                    _owner = _newOwner;
                    emit OwnerUpdated(_prevOwner, _newOwner);
                }
                /// @dev Returns whether owner can be set in the given execution context.
                function _canSetOwner() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPermissions.sol";
            import "../lib/TWStrings.sol";
            /**
             *  @title   Permissions
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
             */
            contract Permissions is IPermissions {
                /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                mapping(bytes32 => mapping(address => bool)) private _hasRole;
                /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                mapping(bytes32 => bytes32) private _getRoleAdmin;
                /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                modifier onlyRole(bytes32 role) {
                    _checkRole(role, msg.sender);
                    _;
                }
                /**
                 *  @notice         Checks whether an account has a particular role.
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRole(bytes32 role, address account) public view override returns (bool) {
                    return _hasRole[role][account];
                }
                /**
                 *  @notice         Checks whether an account has a particular role;
                 *                  role restrictions can be swtiched on and off.
                 *
                 *  @dev            Returns `true` if `account` has been granted `role`.
                 *                  Role restrictions can be swtiched on and off:
                 *                      - If address(0) has ROLE, then the ROLE restrictions
                 *                        don't apply.
                 *                      - If address(0) does not have ROLE, then the ROLE
                 *                        restrictions will apply.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account for which the role is being checked.
                 */
                function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                    if (!_hasRole[role][address(0)]) {
                        return _hasRole[role][account];
                    }
                    return true;
                }
                /**
                 *  @notice         Returns the admin role that controls the specified role.
                 *  @dev            See {grantRole} and {revokeRole}.
                 *                  To change a role's admin, use {_setRoleAdmin}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 */
                function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                    return _getRoleAdmin[role];
                }
                /**
                 *  @notice         Grants a role to an account, if not previously granted.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleGranted Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account to which the role is being granted.
                 */
                function grantRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    if (_hasRole[role][account]) {
                        revert("Can only grant to non holders");
                    }
                    _setupRole(role, account);
                }
                /**
                 *  @notice         Revokes role from an account.
                 *  @dev            Caller must have admin role for the `role`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function revokeRole(bytes32 role, address account) public virtual override {
                    _checkRole(_getRoleAdmin[role], msg.sender);
                    _revokeRole(role, account);
                }
                /**
                 *  @notice         Revokes role from the account.
                 *  @dev            Caller must have the `role`, with caller being the same as `account`.
                 *                  Emits {RoleRevoked Event}.
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param account  Address of the account from which the role is being revoked.
                 */
                function renounceRole(bytes32 role, address account) public virtual override {
                    if (msg.sender != account) {
                        revert("Can only renounce for self");
                    }
                    _revokeRole(role, account);
                }
                /// @dev Sets `adminRole` as `role`'s admin role.
                function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                    bytes32 previousAdminRole = _getRoleAdmin[role];
                    _getRoleAdmin[role] = adminRole;
                    emit RoleAdminChanged(role, previousAdminRole, adminRole);
                }
                /// @dev Sets up `role` for `account`
                function _setupRole(bytes32 role, address account) internal virtual {
                    _hasRole[role][account] = true;
                    emit RoleGranted(role, account, msg.sender);
                }
                /// @dev Revokes `role` from `account`
                function _revokeRole(bytes32 role, address account) internal virtual {
                    _checkRole(role, account);
                    delete _hasRole[role][account];
                    emit RoleRevoked(role, account, msg.sender);
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRole(bytes32 role, address account) internal view virtual {
                    if (!_hasRole[role][account]) {
                        revert(
                            string(
                                abi.encodePacked(
                                    "Permissions: account ",
                                    TWStrings.toHexString(uint160(account), 20),
                                    " is missing role ",
                                    TWStrings.toHexString(uint256(role), 32)
                                )
                            )
                        );
                    }
                }
                /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                    if (!hasRoleWithSwitch(role, account)) {
                        revert(
                            string(
                                abi.encodePacked(
                                    "Permissions: account ",
                                    TWStrings.toHexString(uint160(account), 20),
                                    " is missing role ",
                                    TWStrings.toHexString(uint256(role), 32)
                                )
                            )
                        );
                    }
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPermissionsEnumerable.sol";
            import "./Permissions.sol";
            /**
             *  @title   PermissionsEnumerable
             *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
             *           Also provides interfaces to view all members with a given role, and total count of members.
             */
            contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                /**
                 *  @notice A data structure to store data of members for a given role.
                 *
                 *  @param index    Current index in the list of accounts that have a role.
                 *  @param members  map from index => address of account that has a role
                 *  @param indexOf  map from address => index which the account has.
                 */
                struct RoleMembers {
                    uint256 index;
                    mapping(uint256 => address) members;
                    mapping(address => uint256) indexOf;
                }
                /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                mapping(bytes32 => RoleMembers) private roleMembers;
                /**
                 *  @notice         Returns the role-member from a list of members for a role,
                 *                  at a given index.
                 *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *  @param index    Index in list of current members for the role.
                 *
                 *  @return member  Address of account that has `role`
                 */
                function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                    uint256 currentIndex = roleMembers[role].index;
                    uint256 check;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            if (check == index) {
                                member = roleMembers[role].members[i];
                                return member;
                            }
                            check += 1;
                        } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                            check += 1;
                        }
                    }
                }
                /**
                 *  @notice         Returns total number of accounts that have a role.
                 *  @dev            Returns `count` of accounts that have `role`.
                 *                  See struct {RoleMembers}, and mapping {roleMembers}
                 *
                 *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                 *
                 *  @return count   Total number of accounts that have `role`
                 */
                function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                    uint256 currentIndex = roleMembers[role].index;
                    for (uint256 i = 0; i < currentIndex; i += 1) {
                        if (roleMembers[role].members[i] != address(0)) {
                            count += 1;
                        }
                    }
                    if (hasRole(role, address(0))) {
                        count += 1;
                    }
                }
                /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                ///      See {_removeMember}
                function _revokeRole(bytes32 role, address account) internal override {
                    super._revokeRole(role, account);
                    _removeMember(role, account);
                }
                /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                ///      See {_addMember}
                function _setupRole(bytes32 role, address account) internal override {
                    super._setupRole(role, account);
                    _addMember(role, account);
                }
                /// @dev adds `account` to {roleMembers}, for `role`
                function _addMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].index;
                    roleMembers[role].index += 1;
                    roleMembers[role].members[idx] = account;
                    roleMembers[role].indexOf[account] = idx;
                }
                /// @dev removes `account` from {roleMembers}, for `role`
                function _removeMember(bytes32 role, address account) internal {
                    uint256 idx = roleMembers[role].indexOf[account];
                    delete roleMembers[role].members[idx];
                    delete roleMembers[role].indexOf[account];
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPlatformFee.sol";
            /**
             *  @title   Platform Fee
             *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about platform fees, if desired.
             */
            abstract contract PlatformFee is IPlatformFee {
                /// @dev The address that receives all platform fees from all sales.
                address private platformFeeRecipient;
                /// @dev The % of primary sales collected as platform fees.
                uint16 private platformFeeBps;
                /// @dev Returns the platform fee recipient and bps.
                function getPlatformFeeInfo() public view override returns (address, uint16) {
                    return (platformFeeRecipient, uint16(platformFeeBps));
                }
                /**
                 *  @notice         Updates the platform fee recipient and bps.
                 *  @dev            Caller should be authorized to set platform fee info.
                 *                  See {_canSetPlatformFeeInfo}.
                 *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                 *
                 *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                 *  @param _platformFeeBps         Updated platformFeeBps.
                 */
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                    if (!_canSetPlatformFeeInfo()) {
                        revert("Not authorized");
                    }
                    _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Lets a contract admin update the platform fee recipient and bps
                function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                    if (_platformFeeBps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    platformFeeBps = uint16(_platformFeeBps);
                    platformFeeRecipient = _platformFeeRecipient;
                    emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                }
                /// @dev Returns whether platform fee info can be set in the given execution context.
                function _canSetPlatformFeeInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IPrimarySale.sol";
            /**
             *  @title   Primary Sale
             *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *           primary sales, if desired.
             */
            abstract contract PrimarySale is IPrimarySale {
                /// @dev The address that receives all primary sales value.
                address private recipient;
                /// @dev Returns primary sale recipient address.
                function primarySaleRecipient() public view override returns (address) {
                    return recipient;
                }
                /**
                 *  @notice         Updates primary sale recipient.
                 *  @dev            Caller should be authorized to set primary sales info.
                 *                  See {_canSetPrimarySaleRecipient}.
                 *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                 *
                 *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                 */
                function setPrimarySaleRecipient(address _saleRecipient) external override {
                    if (!_canSetPrimarySaleRecipient()) {
                        revert("Not authorized");
                    }
                    _setupPrimarySaleRecipient(_saleRecipient);
                }
                /// @dev Lets a contract admin set the recipient for all primary sales.
                function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                    recipient = _saleRecipient;
                    emit PrimarySaleRecipientUpdated(_saleRecipient);
                }
                /// @dev Returns whether primary sale recipient can be set in the given execution context.
                function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./interface/IRoyalty.sol";
            /**
             *  @title   Royalty
             *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *           that uses information about royalty fees, if desired.
             *
             *  @dev     The `Royalty` contract is ERC2981 compliant.
             */
            abstract contract Royalty is IRoyalty {
                /// @dev The (default) address that receives all royalty value.
                address private royaltyRecipient;
                /// @dev The (default) % of a sale to take as royalty (in basis points).
                uint16 private royaltyBps;
                /// @dev Token ID => royalty recipient and bps for token
                mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                /**
                 *  @notice   View royalty info for a given token and sale price.
                 *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                 *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                 *  @param salePrice        Sale price of the token.
                 *
                 *  @return receiver        Address of royalty recipient account.
                 *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    virtual
                    override
                    returns (address receiver, uint256 royaltyAmount)
                {
                    (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                    receiver = recipient;
                    royaltyAmount = (salePrice * bps) / 10_000;
                }
                /**
                 *  @notice          View royalty info for a given token.
                 *  @dev             Returns royalty recipient and bps for `_tokenId`.
                 *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                 */
                function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                    RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                    return
                        royaltyForToken.recipient == address(0)
                            ? (royaltyRecipient, uint16(royaltyBps))
                            : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                }
                /**
                 *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                 */
                function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                    return (royaltyRecipient, uint16(royaltyBps));
                }
                /**
                 *  @notice         Updates default royalty recipient and bps.
                 *  @dev            Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                 *
                 *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                 *  @param _royaltyBps         Updated royalty bps.
                 */
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert("Not authorized");
                    }
                    _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                }
                /// @dev Lets a contract admin update the default royalty recipient and bps.
                function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                    if (_royaltyBps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    royaltyRecipient = _royaltyRecipient;
                    royaltyBps = uint16(_royaltyBps);
                    emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                }
                /**
                 *  @notice         Updates default royalty recipient and bps for a particular token.
                 *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                 *                  See {_canSetRoyaltyInfo}.
                 *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                 *
                 *  @param _recipient   Address to be set as royalty recipient for given token Id.
                 *  @param _bps         Updated royalty bps for the token Id.
                 */
                function setRoyaltyInfoForToken(
                    uint256 _tokenId,
                    address _recipient,
                    uint256 _bps
                ) external override {
                    if (!_canSetRoyaltyInfo()) {
                        revert("Not authorized");
                    }
                    _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                function _setupRoyaltyInfoForToken(
                    uint256 _tokenId,
                    address _recipient,
                    uint256 _bps
                ) internal {
                    if (_bps > 10_000) {
                        revert("Exceeds max bps");
                    }
                    royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                    emit RoyaltyForToken(_tokenId, _recipient, _bps);
                }
                /// @dev Returns whether royalty info can be set in the given execution context.
                function _canSetRoyaltyInfo() internal view virtual returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimCondition {
                /**
                 *  @notice The criteria that make up a claim condition.
                 *
                 *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                 *                                        The same claim condition applies until the `startTimestamp`
                 *                                        of the next claim condition.
                 *
                 *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                 *                                        the claim condition.
                 *
                 *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                 *                                        under the claim condition.
                 *
                 *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                 *
                 *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                 *                                        condition.
                 *
                 *  @param pricePerToken                  The price required to pay per token claimed.
                 *
                 *  @param currency                       The currency in which the `pricePerToken` must be paid.
                 *
                 *  @param metadata                       Claim condition metadata.
                 */
                struct ClaimCondition {
                    uint256 startTimestamp;
                    uint256 maxClaimableSupply;
                    uint256 supplyClaimed;
                    uint256 quantityLimitPerWallet;
                    bytes32 merkleRoot;
                    uint256 pricePerToken;
                    address currency;
                    string metadata;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IClaimCondition.sol";
            /**
             *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IClaimConditionMultiPhase is IClaimCondition {
                /**
                 *  @notice The set of all claim conditions, at any given moment.
                 *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                 *
                 *  @param currentStartId           The uid for the first claim condition amongst the current set of
                 *                                  claim conditions. The uid for each next claim condition is one
                 *                                  more than the previous claim condition's uid.
                 *
                 *  @param count                    The total number of phases / claim conditions in the list
                 *                                  of claim conditions.
                 *
                 *  @param conditions                   The claim conditions at a given uid. Claim conditions
                 *                                  are ordered in an ascending order by their `startTimestamp`.
                 *
                 *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                 */
                struct ClaimConditionList {
                    uint256 currentStartId;
                    uint256 count;
                    mapping(uint256 => ClaimCondition) conditions;
                    mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                }
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
             *  for you contract.
             *
             *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
             */
            interface IContractMetadata {
                /// @dev Returns the metadata URI of the contract.
                function contractURI() external view returns (string memory);
                /**
                 *  @dev Sets contract URI for the storefront-level metadata of the contract.
                 *       Only module admin can call this function.
                 */
                function setContractURI(string calldata _uri) external;
                /// @dev Emitted when the contract URI is updated.
                event ContractURIUpdated(string prevURI, string newURI);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IClaimConditionMultiPhase.sol";
            /**
             *  The interface `IDrop1155` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
             *
             *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
             *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
             *  or added to by the contract admin. At any moment, there is only one active claim condition.
             */
            interface IDrop1155 is IClaimConditionMultiPhase {
                /**
                 *  @param proof Prood of concerned wallet's inclusion in an allowlist.
                 *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                 *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                 *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                 */
                struct AllowlistProof {
                    bytes32[] proof;
                    uint256 quantityLimitPerWallet;
                    uint256 pricePerToken;
                    address currency;
                }
                /// @notice Emitted when tokens are claimed.
                event TokensClaimed(
                    uint256 indexed claimConditionIndex,
                    address indexed claimer,
                    address indexed receiver,
                    uint256 tokenId,
                    uint256 quantityClaimed
                );
                /// @notice Emitted when the contract's claim conditions are updated.
                event ClaimConditionsUpdated(uint256 indexed tokenId, ClaimCondition[] claimConditions, bool resetEligibility);
                /**
                 *  @notice Lets an account claim a given quantity of NFTs.
                 *
                 *  @param receiver                       The receiver of the NFTs to claim.
                 *  @param tokenId                        The tokenId of the NFT to claim.
                 *  @param quantity                       The quantity of NFTs to claim.
                 *  @param currency                       The currency in which to pay for the claim.
                 *  @param pricePerToken                  The price per token to pay for the claim.
                 *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                 *                                        of the claim conditions that apply.
                 *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                 */
                function claim(
                    address receiver,
                    uint256 tokenId,
                    uint256 quantity,
                    address currency,
                    uint256 pricePerToken,
                    AllowlistProof calldata allowlistProof,
                    bytes memory data
                ) external payable;
                /**
                 *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                 *
                 *  @param tokenId                  The token ID for which to set mint conditions.
                 *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                 *
                 *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                 *                                  in the new claim conditions being set.
                 *
                 */
                function setClaimConditions(
                    uint256 tokenId,
                    ClaimCondition[] calldata phases,
                    bool resetClaimEligibility
                ) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
             *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
             *  minting a non-zero balance of NFTs of those tokenIds.
             */
            interface ILazyMint {
                /// @dev Emitted when tokens are lazy minted.
                event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                /**
                 *  @notice Lazy mints a given amount of NFTs.
                 *
                 *  @param amount           The number of NFTs to lazy mint.
                 *
                 *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                 *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                 *
                 *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                 *
                 *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                 */
                function lazyMint(
                    uint256 amount,
                    string calldata baseURIForTokens,
                    bytes calldata extraData
                ) external returns (uint256 batchId);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            interface IOperatorFilterRegistry {
                function isOperatorAllowed(address registrant, address operator) external view returns (bool);
                function register(address registrant) external;
                function registerAndSubscribe(address registrant, address subscription) external;
                function registerAndCopyEntries(address registrant, address registrantToCopy) external;
                function unregister(address addr) external;
                function updateOperator(
                    address registrant,
                    address operator,
                    bool filtered
                ) external;
                function updateOperators(
                    address registrant,
                    address[] calldata operators,
                    bool filtered
                ) external;
                function updateCodeHash(
                    address registrant,
                    bytes32 codehash,
                    bool filtered
                ) external;
                function updateCodeHashes(
                    address registrant,
                    bytes32[] calldata codeHashes,
                    bool filtered
                ) external;
                function subscribe(address registrant, address registrantToSubscribe) external;
                function unsubscribe(address registrant, bool copyExistingEntries) external;
                function subscriptionOf(address addr) external returns (address registrant);
                function subscribers(address registrant) external returns (address[] memory);
                function subscriberAt(address registrant, uint256 index) external returns (address);
                function copyEntriesOf(address registrant, address registrantToCopy) external;
                function isOperatorFiltered(address registrant, address operator) external returns (bool);
                function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
                function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
                function filteredOperators(address addr) external returns (address[] memory);
                function filteredCodeHashes(address addr) external returns (bytes32[] memory);
                function filteredOperatorAt(address registrant, uint256 index) external returns (address);
                function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
                function isRegistered(address addr) external returns (bool);
                function codeHashOf(address addr) external returns (bytes32);
            }
            // SPDX-License-Identifier: Apache 2.0
            pragma solidity ^0.8.0;
            interface IOperatorFilterToggle {
                event OperatorRestriction(bool restriction);
                function operatorRestriction() external view returns (bool);
                function setOperatorRestriction(bool restriction) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
             *  information about who the contract's owner is.
             */
            interface IOwnable {
                /// @dev Returns the owner of the contract.
                function owner() external view returns (address);
                /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                function setOwner(address _newOwner) external;
                /// @dev Emitted when a new Owner is set.
                event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             * @dev External interface of AccessControl declared to support ERC165 detection.
             */
            interface IPermissions {
                /**
                 * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                 *
                 * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                 * {RoleAdminChanged} not being emitted signaling this.
                 *
                 * _Available since v3.1._
                 */
                event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                /**
                 * @dev Emitted when `account` is granted `role`.
                 *
                 * `sender` is the account that originated the contract call, an admin role
                 * bearer except when using {AccessControl-_setupRole}.
                 */
                event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Emitted when `account` is revoked `role`.
                 *
                 * `sender` is the account that originated the contract call:
                 *   - if using `revokeRole`, it is the admin role bearer
                 *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                 */
                event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                /**
                 * @dev Returns `true` if `account` has been granted `role`.
                 */
                function hasRole(bytes32 role, address account) external view returns (bool);
                /**
                 * @dev Returns the admin role that controls `role`. See {grantRole} and
                 * {revokeRole}.
                 *
                 * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                 */
                function getRoleAdmin(bytes32 role) external view returns (bytes32);
                /**
                 * @dev Grants `role` to `account`.
                 *
                 * If `account` had not been already granted `role`, emits a {RoleGranted}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function grantRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from `account`.
                 *
                 * If `account` had been granted `role`, emits a {RoleRevoked} event.
                 *
                 * Requirements:
                 *
                 * - the caller must have ``role``'s admin role.
                 */
                function revokeRole(bytes32 role, address account) external;
                /**
                 * @dev Revokes `role` from the calling account.
                 *
                 * Roles are often managed via {grantRole} and {revokeRole}: this function's
                 * purpose is to provide a mechanism for accounts to lose their privileges
                 * if they are compromised (such as when a trusted device is misplaced).
                 *
                 * If the calling account had been granted `role`, emits a {RoleRevoked}
                 * event.
                 *
                 * Requirements:
                 *
                 * - the caller must be `account`.
                 */
                function renounceRole(bytes32 role, address account) external;
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "./IPermissions.sol";
            /**
             * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
             */
            interface IPermissionsEnumerable is IPermissions {
                /**
                 * @dev Returns one of the accounts that have `role`. `index` must be a
                 * value between 0 and {getRoleMemberCount}, non-inclusive.
                 *
                 * Role bearers are not sorted in any particular way, and their ordering may
                 * change at any point.
                 *
                 * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                 * you perform all queries on the same block. See the following
                 * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                 * for more information.
                 */
                function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                /**
                 * @dev Returns the number of accounts that have `role`. Can be used
                 * together with {getRoleMember} to enumerate all bearers of a role.
                 */
                function getRoleMemberCount(bytes32 role) external view returns (uint256);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about platform fees, if desired.
             */
            interface IPlatformFee {
                /// @dev Returns the platform fee bps and recipient.
                function getPlatformFeeInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the fees on primary sales.
                function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                /// @dev Emitted when fee on primary sales is updated.
                event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            /**
             *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
             *  primary sales, if desired.
             */
            interface IPrimarySale {
                /// @dev The adress that receives all primary sales value.
                function primarySaleRecipient() external view returns (address);
                /// @dev Lets a module admin set the default recipient of all primary sales.
                function setPrimarySaleRecipient(address _saleRecipient) external;
                /// @dev Emitted when a new sale recipient is set.
                event PrimarySaleRecipientUpdated(address indexed recipient);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            import "../../eip/interface/IERC2981.sol";
            /**
             *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
             *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
             *  that uses information about royalty fees, if desired.
             *
             *  The `Royalty` contract is ERC2981 compliant.
             */
            interface IRoyalty is IERC2981 {
                struct RoyaltyInfo {
                    address recipient;
                    uint256 bps;
                }
                /// @dev Returns the royalty recipient and fee bps.
                function getDefaultRoyaltyInfo() external view returns (address, uint16);
                /// @dev Lets a module admin update the royalty bps and recipient.
                function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                function setRoyaltyInfoForToken(
                    uint256 tokenId,
                    address recipient,
                    uint256 bps
                ) external;
                /// @dev Returns the royalty recipient for a particular token Id.
                function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                /// @dev Emitted when royalty info is updated.
                event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                /// @dev Emitted when royalty recipient for tokenId is set
                event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            interface IWETH {
                function deposit() external payable;
                function withdraw(uint256 amount) external;
                function transfer(address to, uint256 value) external returns (bool);
            }
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity ^0.8.0;
            // Helper interfaces
            import { IWETH } from "../interfaces/IWETH.sol";
            import "../openzeppelin-presets/token/ERC20/utils/SafeERC20.sol";
            library CurrencyTransferLib {
                using SafeERC20 for IERC20;
                /// @dev The address interpreted as native token of the chain.
                address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /// @dev Transfers a given amount of currency.
                function transferCurrency(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount
                ) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        safeTransferNativeToken(_to, _amount);
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfers a given amount of currency. (With native token wrapping)
                function transferCurrencyWithWrapper(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount,
                    address _nativeTokenWrapper
                ) internal {
                    if (_amount == 0) {
                        return;
                    }
                    if (_currency == NATIVE_TOKEN) {
                        if (_from == address(this)) {
                            // withdraw from weth then transfer withdrawn native token to recipient
                            IWETH(_nativeTokenWrapper).withdraw(_amount);
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        } else if (_to == address(this)) {
                            // store native currency in weth
                            require(_amount == msg.value, "msg.value != amount");
                            IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                        } else {
                            safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                        }
                    } else {
                        safeTransferERC20(_currency, _from, _to, _amount);
                    }
                }
                /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                function safeTransferERC20(
                    address _currency,
                    address _from,
                    address _to,
                    uint256 _amount
                ) internal {
                    if (_from == _to) {
                        return;
                    }
                    if (_from == address(this)) {
                        IERC20(_currency).safeTransfer(_to, _amount);
                    } else {
                        IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                    }
                }
                /// @dev Transfers `amount` of native token to `to`.
                function safeTransferNativeToken(address to, uint256 value) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    require(success, "native token transfer failed");
                }
                /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                function safeTransferNativeTokenWithWrapper(
                    address to,
                    uint256 value,
                    address _nativeTokenWrapper
                ) internal {
                    // solhint-disable avoid-low-level-calls
                    // slither-disable-next-line low-level-calls
                    (bool success, ) = to.call{ value: value }("");
                    if (!success) {
                        IWETH(_nativeTokenWrapper).deposit{ value: value }();
                        IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/utils/cryptography/MerkleProof.sol
            // Copied from https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
            pragma solidity ^0.8.0;
            /**
             * @dev These functions deal with verification of Merkle Trees proofs.
             *
             * The proofs can be generated using the JavaScript library
             * https://github.com/miguelmota/merkletreejs[merkletreejs].
             * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
             *
             * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
             *
             * Source: https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
             */
            library MerkleProof {
                /**
                 * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                 * defined by `root`. For this, a `proof` must be provided, containing
                 * sibling hashes on the branch from the leaf to the root of the tree. Each
                 * pair of leaves and each pair of pre-images are assumed to be sorted.
                 */
                function verify(
                    bytes32[] memory proof,
                    bytes32 root,
                    bytes32 leaf
                ) internal pure returns (bool, uint256) {
                    bytes32 computedHash = leaf;
                    uint256 index = 0;
                    for (uint256 i = 0; i < proof.length; i++) {
                        index *= 2;
                        bytes32 proofElement = proof[i];
                        if (computedHash <= proofElement) {
                            // Hash(current computed hash + current element of the proof)
                            computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                        } else {
                            // Hash(current element of the proof + current computed hash)
                            computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                            index += 1;
                        }
                    }
                    // Check if the computed hash (root) is equal to the provided root
                    return (computedHash == root, index);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library TWAddress {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{ value: value }(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev String operations.
             */
            library TWStrings {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    // Inspired by OraclizeAPI's implementation - MIT licence
                    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                    if (value == 0) {
                        return "0";
                    }
                    uint256 temp = value;
                    uint256 digits;
                    while (temp != 0) {
                        digits++;
                        temp /= 10;
                    }
                    bytes memory buffer = new bytes(digits);
                    while (value != 0) {
                        digits -= 1;
                        buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                        value /= 10;
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    if (value == 0) {
                        return "0x00";
                    }
                    uint256 temp = value;
                    uint256 length = 0;
                    while (temp != 0) {
                        length++;
                        temp >>= 8;
                    }
                    return toHexString(value, length);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    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_SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    return string(buffer);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.0 (metatx/ERC2771Context.sol)
            pragma solidity ^0.8.11;
            import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
            /**
             * @dev Context variant with ERC2771 support.
             */
            abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                mapping(address => bool) private _trustedForwarder;
                function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                    __Context_init_unchained();
                    __ERC2771Context_init_unchained(trustedForwarder);
                }
                function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                    for (uint256 i = 0; i < trustedForwarder.length; i++) {
                        _trustedForwarder[trustedForwarder[i]] = true;
                    }
                }
                function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                    return _trustedForwarder[forwarder];
                }
                function _msgSender() internal view virtual override returns (address sender) {
                    if (isTrustedForwarder(msg.sender)) {
                        // The assembly code is more direct than the Solidity version using `abi.decode`.
                        assembly {
                            sender := shr(96, calldataload(sub(calldatasize(), 20)))
                        }
                    } else {
                        return super._msgSender();
                    }
                }
                function _msgData() internal view virtual override returns (bytes calldata) {
                    if (isTrustedForwarder(msg.sender)) {
                        return msg.data[:msg.data.length - 20];
                    } else {
                        return super._msgData();
                    }
                }
                uint256[49] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
            pragma solidity ^0.8.0;
            import "../../../../eip/interface/IERC20.sol";
            import "../../../../lib/TWAddress.sol";
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using TWAddress for address;
                function safeTransfer(
                    IERC20 token,
                    address to,
                    uint256 value
                ) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
                function safeTransferFrom(
                    IERC20 token,
                    address from,
                    address to,
                    uint256 value
                ) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    require(
                        (value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
                function safeIncreaseAllowance(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    uint256 newAllowance = token.allowance(address(this), spender) + value;
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
                function safeDecreaseAllowance(
                    IERC20 token,
                    address spender,
                    uint256 value
                ) internal {
                    unchecked {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                        uint256 newAllowance = oldAllowance - value;
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                    }
                }
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) {
                        // Return data is optional
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
            pragma solidity ^0.8.0;
            import "../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Interface for the NFT Royalty Standard.
             *
             * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
             * support for royalty payments across all NFT marketplaces and ecosystem participants.
             *
             * _Available since v4.5._
             */
            interface IERC2981Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                 * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                 */
                function royaltyInfo(uint256 tokenId, uint256 salePrice)
                    external
                    view
                    returns (address receiver, uint256 royaltyAmount);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
            pragma solidity ^0.8.2;
            import "../../utils/AddressUpgradeable.sol";
            /**
             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
             *
             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
             * case an upgrade adds a module that needs to be initialized.
             *
             * For example:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * contract MyToken is ERC20Upgradeable {
             *     function initialize() initializer public {
             *         __ERC20_init("MyToken", "MTK");
             *     }
             * }
             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
             *     function initializeV2() reinitializer(2) public {
             *         __ERC20Permit_init("MyToken");
             *     }
             * }
             * ```
             *
             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
             *
             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
             *
             * [CAUTION]
             * ====
             * Avoid leaving a contract uninitialized.
             *
             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * /// @custom:oz-upgrades-unsafe-allow constructor
             * constructor() {
             *     _disableInitializers();
             * }
             * ```
             * ====
             */
            abstract contract Initializable {
                /**
                 * @dev Indicates that the contract has been initialized.
                 * @custom:oz-retyped-from bool
                 */
                uint8 private _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool private _initializing;
                /**
                 * @dev Triggered when the contract has been initialized or reinitialized.
                 */
                event Initialized(uint8 version);
                /**
                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                 * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                 */
                modifier initializer() {
                    bool isTopLevelCall = !_initializing;
                    require(
                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                        "Initializable: contract is already initialized"
                    );
                    _initialized = 1;
                    if (isTopLevelCall) {
                        _initializing = true;
                    }
                    _;
                    if (isTopLevelCall) {
                        _initializing = false;
                        emit Initialized(1);
                    }
                }
                /**
                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                 * used to initialize parent contracts.
                 *
                 * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                 * initialization step. This is essential to configure modules that are added through upgrades and that require
                 * initialization.
                 *
                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                 * a contract, executing them in the right order is up to the developer or operator.
                 */
                modifier reinitializer(uint8 version) {
                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                    _initialized = version;
                    _initializing = true;
                    _;
                    _initializing = false;
                    emit Initialized(version);
                }
                /**
                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                 */
                modifier onlyInitializing() {
                    require(_initializing, "Initializable: contract is not initializing");
                    _;
                }
                /**
                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                 * through proxies.
                 */
                function _disableInitializers() internal virtual {
                    require(!_initializing, "Initializable: contract is initializing");
                    if (_initialized < type(uint8).max) {
                        _initialized = type(uint8).max;
                        emit Initialized(type(uint8).max);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/ERC1155.sol)
            pragma solidity ^0.8.0;
            import "./IERC1155Upgradeable.sol";
            import "./IERC1155ReceiverUpgradeable.sol";
            import "./extensions/IERC1155MetadataURIUpgradeable.sol";
            import "../../utils/AddressUpgradeable.sol";
            import "../../utils/ContextUpgradeable.sol";
            import "../../utils/introspection/ERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the basic standard multi-token.
             * See https://eips.ethereum.org/EIPS/eip-1155
             * Originally based on code by Enjin: https://github.com/enjin/erc-1155
             *
             * _Available since v3.1._
             */
            contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
                using AddressUpgradeable for address;
                // Mapping from token ID to account balances
                mapping(uint256 => mapping(address => uint256)) private _balances;
                // Mapping from account to operator approvals
                mapping(address => mapping(address => bool)) private _operatorApprovals;
                // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
                string private _uri;
                /**
                 * @dev See {_setURI}.
                 */
                function __ERC1155_init(string memory uri_) internal onlyInitializing {
                    __ERC1155_init_unchained(uri_);
                }
                function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
                    _setURI(uri_);
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                    return
                        interfaceId == type(IERC1155Upgradeable).interfaceId ||
                        interfaceId == type(IERC1155MetadataURIUpgradeable).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) public view virtual override returns (string memory) {
                    return _uri;
                }
                /**
                 * @dev See {IERC1155-balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
                    require(account != address(0), "ERC1155: address zero is not a valid owner");
                    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
                    override
                    returns (uint256[] memory)
                {
                    require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
                    uint256[] memory batchBalances = new uint256[](accounts.length);
                    for (uint256 i = 0; i < accounts.length; ++i) {
                        batchBalances[i] = balanceOf(accounts[i], ids[i]);
                    }
                    return batchBalances;
                }
                /**
                 * @dev See {IERC1155-setApprovalForAll}.
                 */
                function setApprovalForAll(address operator, bool approved) public virtual override {
                    _setApprovalForAll(_msgSender(), operator, approved);
                }
                /**
                 * @dev See {IERC1155-isApprovedForAll}.
                 */
                function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
                    return _operatorApprovals[account][operator];
                }
                /**
                 * @dev See {IERC1155-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner nor approved"
                    );
                    _safeTransferFrom(from, to, id, amount, data);
                }
                /**
                 * @dev See {IERC1155-safeBatchTransferFrom}.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) public virtual override {
                    require(
                        from == _msgSender() || isApprovedForAll(from, _msgSender()),
                        "ERC1155: caller is not token owner nor approved"
                    );
                    _safeBatchTransferFrom(from, to, ids, amounts, data);
                }
                /**
                 * @dev Transfers `amount` 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 `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 amount,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, from, to, id, amount);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, 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.
                 */
                function _safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    require(to != address(0), "ERC1155: transfer to the zero address");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; ++i) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                        _balances[id][to] += amount;
                    }
                    emit TransferBatch(operator, from, to, ids, amounts);
                    _afterTokenTransfer(operator, from, to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
                }
                /**
                 * @dev Sets a new URI for all token types, by relying on the token type ID
                 * substitution mechanism
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
                 *
                 * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
                 * URI or any of the amounts in the JSON file at said URI will be replaced by
                 * clients with the token type ID.
                 *
                 * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
                 * interpreted by clients as
                 * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
                 * for token type ID 0x4cce0.
                 *
                 * See {uri}.
                 *
                 * Because these URIs cannot be meaningfully represented by the {URI} event,
                 * this function emits no events.
                 */
                function _setURI(string memory newuri) internal virtual {
                    _uri = newuri;
                }
                /**
                 * @dev Creates `amount` tokens of token 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 amount,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _balances[id][to] += amount;
                    emit TransferSingle(operator, address(0), to, id, amount);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function _mintBatch(
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {
                    require(to != address(0), "ERC1155: mint to the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                    for (uint256 i = 0; i < ids.length; i++) {
                        _balances[ids[i]][to] += amounts[i];
                    }
                    emit TransferBatch(operator, address(0), to, ids, amounts);
                    _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                    _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
                }
                /**
                 * @dev Destroys `amount` tokens of token type `id` from `from`
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `from` must have at least `amount` tokens of token type `id`.
                 */
                function _burn(
                    address from,
                    uint256 id,
                    uint256 amount
                ) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    address operator = _msgSender();
                    uint256[] memory ids = _asSingletonArray(id);
                    uint256[] memory amounts = _asSingletonArray(amount);
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    uint256 fromBalance = _balances[id][from];
                    require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                    unchecked {
                        _balances[id][from] = fromBalance - amount;
                    }
                    emit TransferSingle(operator, from, address(0), id, amount);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 */
                function _burnBatch(
                    address from,
                    uint256[] memory ids,
                    uint256[] memory amounts
                ) internal virtual {
                    require(from != address(0), "ERC1155: burn from the zero address");
                    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                    address operator = _msgSender();
                    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                    for (uint256 i = 0; i < ids.length; i++) {
                        uint256 id = ids[i];
                        uint256 amount = amounts[i];
                        uint256 fromBalance = _balances[id][from];
                        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                        unchecked {
                            _balances[id][from] = fromBalance - amount;
                        }
                    }
                    emit TransferBatch(operator, from, address(0), ids, amounts);
                    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
                }
                /**
                 * @dev Approve `operator` to operate on all of `owner` tokens
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function _setApprovalForAll(
                    address owner,
                    address operator,
                    bool approved
                ) internal virtual {
                    require(owner != operator, "ERC1155: setting approval status for self");
                    _operatorApprovals[owner][operator] = approved;
                    emit ApprovalForAll(owner, operator, approved);
                }
                /**
                 * @dev Hook that is called before any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `ids` and `amounts` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                /**
                 * @dev Hook that is called after any token transfer. This includes minting
                 * and burning, as well as batched variants.
                 *
                 * The same hook is called on both single and batched variants. For single
                 * transfers, the length of the `id` and `amount` arrays will be 1.
                 *
                 * Calling conditions (for each `id` and `amount` pair):
                 *
                 * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * of token type `id` will be  transferred to `to`.
                 * - When `from` is zero, `amount` tokens of token type `id` will be minted
                 * for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
                 * will be burned.
                 * - `from` and `to` are never both zero.
                 * - `ids` and `amounts` have the same, non-zero length.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _afterTokenTransfer(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) internal virtual {}
                function _doSafeTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non ERC1155Receiver implementer");
                        }
                    }
                }
                function _doSafeBatchTransferAcceptanceCheck(
                    address operator,
                    address from,
                    address to,
                    uint256[] memory ids,
                    uint256[] memory amounts,
                    bytes memory data
                ) private {
                    if (to.isContract()) {
                        try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                            bytes4 response
                        ) {
                            if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                                revert("ERC1155: ERC1155Receiver rejected tokens");
                            }
                        } catch Error(string memory reason) {
                            revert(reason);
                        } catch {
                            revert("ERC1155: transfer to non ERC1155Receiver implementer");
                        }
                    }
                }
                function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                    uint256[] memory array = new uint256[](1);
                    array[0] = element;
                    return array;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[47] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev _Available since v3.1._
             */
            interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
                /**
                 * @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 v4.7.0) (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165Upgradeable.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155Upgradeable is IERC165Upgradeable {
                /**
                 * @dev Emitted when `value` tokens of token 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 amount 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 `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * 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 `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 amount,
                    bytes calldata data
                ) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` 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 amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
            pragma solidity ^0.8.0;
            import "../IERC1155Upgradeable.sol";
            /**
             * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
             * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
                /**
                 * @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 v4.7.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library AddressUpgradeable {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            import "../proxy/utils/Initializable.sol";
            /**
             * @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 ContextUpgradeable is Initializable {
                function __Context_init() internal onlyInitializing {
                }
                function __Context_init_unchained() internal onlyInitializing {
                }
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
            pragma solidity ^0.8.0;
            import "./AddressUpgradeable.sol";
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Provides a function to batch together multiple calls in a single external call.
             *
             * _Available since v4.1._
             */
            abstract contract MulticallUpgradeable is Initializable {
                function __Multicall_init() internal onlyInitializing {
                }
                function __Multicall_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev Receives and executes a batch of function calls on this contract.
                 */
                function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                    results = new bytes[](data.length);
                    for (uint256 i = 0; i < data.length; i++) {
                        results[i] = _functionDelegateCall(address(this), data[i]);
                    }
                    return results;
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                    require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev String operations.
             */
            library StringsUpgradeable {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                uint8 private constant _ADDRESS_LENGTH = 20;
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    // Inspired by OraclizeAPI's implementation - MIT licence
                    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                    if (value == 0) {
                        return "0";
                    }
                    uint256 temp = value;
                    uint256 digits;
                    while (temp != 0) {
                        digits++;
                        temp /= 10;
                    }
                    bytes memory buffer = new bytes(digits);
                    while (value != 0) {
                        digits -= 1;
                        buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                        value /= 10;
                    }
                    return string(buffer);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    if (value == 0) {
                        return "0x00";
                    }
                    uint256 temp = value;
                    uint256 length = 0;
                    while (temp != 0) {
                        length++;
                        temp >>= 8;
                    }
                    return toHexString(value, length);
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    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_SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    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);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                function __ERC165_init() internal onlyInitializing {
                }
                function __ERC165_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165Upgradeable).interfaceId;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165Upgradeable {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }