ETH Price: $2,456.42 (+2.31%)

Transaction Decoder

Block:
18264692 at Oct-02-2023 06:38:59 PM +UTC
Transaction Fee:
0.002570137594019784 ETH $6.31
Gas Used:
111,816 Gas / 22.985418849 Gwei

Emitted Events:

171 MultiRelease.OnVoucherUsed( projectId=357226955, account=[Sender] 0x80acb3b55132c4fe408f8bcda583aaf98824e206, voucherId=5146 )
172 Ringers962Edition.TransferSingle( operator=[Receiver] MultiRelease, from=0x00000000...000000000, to=[Sender] 0x80acb3b55132c4fe408f8bcda583aaf98824e206, id=1, value=1 )
173 MultiRelease.OnTokenClaim( projectId=357226955, account=[Sender] 0x80acb3b55132c4fe408f8bcda583aaf98824e206, tokenId=1, value=0, minted=True )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
5.942482102753096581 Eth5.942493284353096581 Eth0.0000111816
0x80Acb3b5...98824e206
0.480127017445023812 Eth
Nonce: 218
0.477556879851004028 Eth
Nonce: 219
0.002570137594019784
0x8f173F62...96B3D11b7
0xCAc5bc39...25D944146

Execution Trace

MultiRelease.claimSpecifiedLazyMint( voucher=[{name:projectId, type:uint256, order:1, indexed:false, value:357226955, valueString:357226955}, {name:walletAddress, type:address, order:2, indexed:false, value:0x80Acb3b55132C4fe408f8BCDa583AAF98824e206, valueString:0x80Acb3b55132C4fe408f8BCDa583AAF98824e206}, {name:voucherId, type:uint256, order:3, indexed:false, value:5146, valueString:5146}, {name:price, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:validUntil, type:uint256, order:5, indexed:false, value:1696281915, valueString:1696281915}, {name:tokenId, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:signature, type:bytes, order:7, indexed:false, value:0xF81643CDF76660E86036203970AAA1B9A7915FBB9E2E0E7314C93B1DB24EA2A50ED6066B3DBFBFF9FA996D73A7250D4C126D77D098E3013E8CE552C7A3A0811A1C, valueString:0xF81643CDF76660E86036203970AAA1B9A7915FBB9E2E0E7314C93B1DB24EA2A50ED6066B3DBFBFF9FA996D73A7250D4C126D77D098E3013E8CE552C7A3A0811A1C}] )
  • Null: 0x000...001.9cada75a( )
  • Ringers962Edition.mint( to=0x80Acb3b55132C4fe408f8BCDa583AAF98824e206, tokenId=1 ) => ( 1 )
    File 1 of 2: MultiRelease
    //SPDX-License-Identifier: Unlicense
    pragma solidity 0.8.17;
    pragma abicoder v2;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import {TimerLib} from "../libraries/Timer/TimerLib.sol";
    import {IERC721CreatorMintPermissions} from "@manifoldxyz/creator-core-solidity/contracts/permissions/ERC721/IERC721CreatorMintPermissions.sol";
    import {IERC721CreatorCore} from "@manifoldxyz/creator-core-solidity/contracts/core/IERC721CreatorCore.sol";
    import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import {ISpecifiedMinter} from "./Erc721/ISpecifiedMinter.sol";
    /// @notice Represents the settings for an auction
    struct NftAuctionSettings {
        // the time at which the auction would close (will be updating)
        uint256 initialAuctionSeconds;
        // the time at which the auction would close (will be updating)
        uint256 floor;
        // The minimum amount of time left in an auction after a new bid is created
        uint256 timeBufferSeconds;
        // the token id for this auction
        uint256 tokenId;
        // The minimum percentage difference between the last bid amount and the current bid. (1-100)
        uint256 minBidIncrementPercentage;
    }
    /// @notice Represents an auction project
    struct NftAuction {
        NftAuctionSettings settings;
        // the token id for this auction
        uint256 startTime;
        // the time at which the auction would close (will be updating)
        uint256 closeTime;
        // the highest bid, used specifically for auctions
        uint256 highBid;
        // the highest bidder, used specifically for auctions
        address highBidder;
    }
    /// @notice Represents a ranged project
    struct NftRangedProjectState {
        // used for ranged release to specify the start of the range
        uint256 rangeStart;
        // used for ranged release to specify the end of the range
        uint256 rangeEnd;
        // used specifically for ranged release
        uint256 pointer;
    }
    /// @notice Represents an input to create/update a project
    struct NftProjectInput {
        // the id of the project (should use product id from storyblok)
        uint256 id;
        // the wallet of the project
        address wallet;
        // the nft contract of the project
        address nftContract;
        // the time at which the contract would be closed
        uint256 closeTime;
        // allows us to pause the project if needed
        bool paused;
        // the custodial for the tokens in this project, if applicable
        address custodial;
        // we can limit items to be claimed from a release by specifying a limit.
        uint256 countLimit;
    }
    /// @notice Represents an NFT project
    struct NftProject {
        // the curator who created the project
        address curator;
        // the time the project was created
        uint256 timestamp;
        // the type of the project
        uint256 projectType;
        // the id of the project (should use product id from storyblok)
        uint256 id;
        // the wallet of the project
        address wallet;
        // the nft contract of the project
        address nftContract;
        // the time at which the contract would be closed
        uint256 closeTime;
        // allows us to pause the project if needed
        bool paused;
        // the custodial for the tokens in this project, if applicable
        address custodial;
        // counts the items claimed from this release.
        uint256 count;
        // we can limit items to be claimed from a release by specifying a limit.
        uint256 countLimit;
    }
    /// @notice Represents a voucher with definitions that allows the holder to claim an NFT
    struct NFTVoucher {
        /// @notice the id of the project, allows us to scope projects.
        uint256 projectId;
        /// @notice (optional) used to lock voucher usage to specific wallet address.
        address walletAddress;
        /// @notice the identifier of the voucher, used to prevent double usage.
        uint256 voucherId;
        /// @notice (optional) The minimum price (in wei) that the NFT creator is willing to accept for the initial sale of this NFT.
        uint256 price;
        /// @notice (optional) allows us to restrict voucher usage.
        uint256 validUntil;
        /// @notice (optional) allows us to restrict voucher usage.
        uint256 tokenId;
        /// @notice the EIP-712 signature of all other fields in the NFTVoucher struct. For a voucher to be valid, it must be signed by an account with the SIGNER_ROLE.
        bytes signature;
    }
    /// @notice Represents the state of a project
    struct ProjectStateOutput {
        uint256 time;
        NftProject project;
        NftAuction auction;
        NftRangedProjectState ranged;
    }
    /// @title a multi release contract supporting multiple release formats
    /// @author Liron Navon
    /// @notice this contract has a complicated access system, please contact owner for support
    /// @dev This contract heavily relies on vouchers with valid signatures.
    contract MultiRelease is
        Ownable,
        ReentrancyGuard,
        EIP712,
        AccessControl,
        IERC721CreatorMintPermissions
    {
        /// @dev roles for access control
        bytes32 private constant SIGNER_ROLE = keccak256("SIGNER_ROLE");
        bytes32 private constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
        bytes32 private constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE");
        /// @dev release types for project types
        uint256 private constant AUCTION_PROJECT = 1;
        uint256 private constant SPECIFIED_PROJECT = 2;
        uint256 private constant RANGED_PROJECT = 3;
        uint256 private constant LAZY_MINT_PROJECT = 4;
        uint256 private constant SPECIFIED_LAZY_MINT_PROJECT = 5;
        /// @dev for domain separation (EIP712)
        string private constant SIGNING_DOMAIN = "AvantArte NFT Voucher";
        string private constant SIGNATURE_VERSION = "1";
        /// @notice vouchers which are already used
        mapping(uint256 => address) public usedVouchers;
        /// @notice mapping of projectId => project
        mapping(uint256 => NftProject) private projects;
        /// @notice mapping of projectId => auction info - used only for auctions
        mapping(uint256 => NftAuction) private auctions;
        /// @notice mapping of projectId => auction project - used only for auctions
        mapping(uint256 => NftRangedProjectState) private rangedProjects;
        /// @notice mapping of address => address - used to verify minting using manifold
        mapping(address => address) private pendingMints;
        /// @notice an event that represents when funds have been withdrawn from the contract
        event OnWithdraw(
            uint256 indexed projectId,
            address indexed account,
            uint256 value
        );
        /// @notice an event that represents when a token is claimed
        event OnTokenClaim(
            uint256 indexed projectId,
            address indexed account,
            uint256 tokenId,
            uint256 value,
            bool minted
        );
        /// @notice an event that represents when a bid happens
        event OnAuctionBid(
            uint256 indexed projectId,
            address indexed account,
            uint256 tokenId,
            uint256 value
        );
        /// @notice an event that represents when an auction start
        event OnAuctionStart(
            uint256 indexed projectId,
            address indexed account,
            uint256 tokenId
        );
        /// @notice an event to call when the auction is closed manually
        event OnAuctionClose(uint256 indexed projectId, address indexed account);
        /// @notice an event to call when a user dropped from the auction
        event OnAuctionOutBid(
            uint256 indexed projectId,
            address indexed account,
            uint256 tokenId,
            uint256 value
        );
        /// @notice an event that happens when a project is created
        event OnProjectCreated(
            uint256 indexed projectId,
            address indexed account,
            uint256 indexed projectType
        );
        /// @notice an event that happens when a voucher is used
        event OnVoucherUsed(
            uint256 indexed projectId,
            address indexed account,
            uint256 voucherId
        );
        // solhint-disable-next-line no-empty-blocks
        constructor() ReentrancyGuard() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {}
        /// @notice creates a project in which we give a range of tokens
        /// @param project the input to create this project
        /// @param rangeStart the first token in the range
        /// @param rangeEnd the last token in the range
        /// @param pointer where we start counting from, in a new project it should be same as rangeStart
        function setRangedProject(
            NftProjectInput calldata project,
            uint256 rangeStart,
            uint256 rangeEnd,
            uint256 pointer
        ) external onlyRole(ADMIN_ROLE) {
            _setProject(project, RANGED_PROJECT);
            rangedProjects[project.id].rangeStart = rangeStart;
            rangedProjects[project.id].rangeEnd = rangeEnd;
            rangedProjects[project.id].pointer = pointer;
        }
        /// @notice creates a project in which we expect to be given a contract of type manifold creator
        /// @param project the input to create this project
        function setLazyMintProject(NftProjectInput calldata project)
            external
            onlyRole(ADMIN_ROLE)
        {
            _setProject(project, LAZY_MINT_PROJECT);
        }
        /// @notice creates a project in which we expect to be given a contract that implements the ISpecifiedMinter interface
        /// @param project the input to create this project
        function setSpecifiedLazyMintProject(NftProjectInput calldata project)
            external
            onlyRole(ADMIN_ROLE)
        {
            _setProject(project, SPECIFIED_LAZY_MINT_PROJECT);
        }
        /// @notice creates a project in which we expect to be given a tokenId from the voucher
        /// @param project the input to create this project
        function setSpecifiedProject(NftProjectInput calldata project)
            external
            onlyRole(ADMIN_ROLE)
        {
            _setProject(project, SPECIFIED_PROJECT);
        }
        /// @notice creates a project which is an auction
        /// @param project the input to create this project
        /// @param auctionSettings extra settings, releated to the auction
        function setAuctionProject(
            NftProjectInput calldata project,
            NftAuctionSettings memory auctionSettings
        ) external onlyRole(ADMIN_ROLE) {
            _setProject(project, AUCTION_PROJECT);
            // settings specific to auction project
            auctions[project.id].settings = auctionSettings;
        }
        /// @notice allows an admin to withdraw funds from the contract, be careful as this can break functionality
        /// @dev extra care was taken to make sure the contract has only the funds reqired to function
        /// @param to the address to get the funds
        /// @param value the amount of funds to withdraw
        /// @param projectId the project id this withdrawal is based off
        function withdraw(
            address to,
            uint256 value,
            uint256 projectId
        ) external onlyRole(WITHDRAWER_ROLE) {
            _withdraw(to, value, projectId);
        }
        /// @dev makes sure the project exists
        /// @param projectId the id of the project
        modifier onlyExistingProject(uint256 projectId) {
            require(projects[projectId].timestamp != 0, "Nonexisting project");
            _;
        }
        /// @dev makes sure the project is of the right type
        /// @param projectId the id of the project
        /// @param projectType type id of the project
        modifier onlyProjectOfType(uint256 projectId, uint256 projectType) {
            require(projects[projectId].timestamp != 0, "Nonexisting project");
            require(
                projects[projectId].projectType == projectType,
                "Wrong project type"
            );
            _;
        }
        /// @dev makes sure the project is active
        /// @param projectId the id of the project
        modifier onlyActiveProjects(uint256 projectId) {
            // check if the project is paused
            require(!projects[projectId].paused, "Project is paused");
            // check if the project has a closeTime, and if so check if it passed
            if (projects[projectId].closeTime > 0) {
                require(
                    projects[projectId].closeTime >= TimerLib._now(),
                    "Project is over"
                );
            }
            // check if the project has a countLimit, and if it's reached
            if (projects[projectId].countLimit > 0) {
                require(
                    projects[projectId].countLimit > projects[projectId].count,
                    "Project at count limit"
                );
            }
            _;
        }
        /// @dev makes sure voucher was never used
        /// @param voucherId the id of the voucher
        modifier onlyUnusedVouchers(uint256 voucherId) {
            require(usedVouchers[voucherId] == address(0), "Used voucher");
            _;
        }
        /// @dev makes sure the voucher is verified
        /// @param voucher the voucher to validates
        modifier onlyVerifiedVouchers(NFTVoucher calldata voucher) {
            // check authorized signer
            require(
                hasRole(SIGNER_ROLE, _recoverVoucherSigner(voucher)),
                "Unauthorized signer"
            );
            // check payment
            if (voucher.price > 0) {
                require(msg.value >= voucher.price, "Insufficient funds");
            }
            if (voucher.validUntil > 0) {
                require(voucher.validUntil >= TimerLib._now(), "Voucher expired");
            }
            // check wallet restriction
            if (voucher.walletAddress != address(0)) {
                require(voucher.walletAddress == msg.sender, "Unauthorized wallet");
            }
            _;
        }
        /// @notice sets the project as paused
        /// @param projectId the id of the project
        /// @param paused is the project paused
        function setPaused(uint256 projectId, bool paused)
            external
            onlyExistingProject(projectId)
            onlyRole(ADMIN_ROLE)
        {
            projects[projectId].paused = paused;
        }
        /// @dev starts the auction
        /// @param projectId the id of the project
        function _startAuction(uint256 projectId) private {
            // set start time
            auctions[projectId].startTime = TimerLib._now();
            // set end time
            auctions[projectId].closeTime =
                TimerLib._now() +
                auctions[projectId].settings.initialAuctionSeconds;
            emit OnAuctionStart(
                projectId,
                msg.sender,
                auctions[projectId].settings.tokenId
            );
        }
        /// @notice starts the auction manualy
        /// @param projectId the id of the project
        function startAuction(uint256 projectId)
            external
            onlyProjectOfType(projectId, AUCTION_PROJECT)
            onlyRole(ADMIN_ROLE)
        {
            _startAuction(projectId);
        }
        /// @notice close the auction manually
        /// @param projectId the id of the project
        function closeAuction(uint256 projectId)
            external
            onlyProjectOfType(projectId, AUCTION_PROJECT)
            onlyRole(ADMIN_ROLE)
        {
            auctions[projectId].closeTime = TimerLib._now();
            emit OnAuctionClose({projectId: projectId, account: msg.sender});
        }
        /// @notice start the project with a given time
        /// @param projectId the id of the project
        /// @param timeSeconds the time, in seconds
        function startWithTime(uint256 projectId, uint256 timeSeconds)
            external
            onlyExistingProject(projectId)
            onlyRole(ADMIN_ROLE)
        {
            projects[projectId].paused = false;
            projects[projectId].closeTime = TimerLib._now() + timeSeconds;
        }
        function getProjectState(uint256 projectId)
            external
            view
            returns (ProjectStateOutput memory state)
        {
            return
                ProjectStateOutput({
                    time: TimerLib._now(),
                    project: projects[projectId],
                    auction: auctions[projectId],
                    ranged: rangedProjects[projectId]
                });
        }
        /// @dev in order to make a bid in an auction, a user must pass a certain threshhold, this function calculates it
        /// @param projectId the id of the auction project
        function _getAuctionThreshHold(uint256 projectId)
            private
            view
            returns (uint256)
        {
            return
                auctions[projectId].highBid +
                (auctions[projectId].highBid *
                    auctions[projectId].settings.minBidIncrementPercentage) /
                100;
        }
        /// @notice validates and marks voucher as used
        /// @param voucher the voucher to use
        function _useVoucher(NFTVoucher calldata voucher)
            private
            onlyUnusedVouchers(voucher.voucherId)
            onlyVerifiedVouchers(voucher)
        {
            usedVouchers[voucher.voucherId] = msg.sender;
            projects[voucher.projectId].count += 1;
            emit OnVoucherUsed(voucher.projectId, msg.sender, voucher.voucherId);
        }
        /// @dev take the funds if required, validate required payments before calling this
        /// @param to the wallet to get the funds
        /// @param amount the amount of funds to withdraw
        /// @param projectId the project id related to the funds
        function _withdraw(
            address to,
            uint256 amount,
            uint256 projectId
        ) private {
            emit OnWithdraw(projectId, to, amount);
            /// solhint-disable-next-line avoid-low-level-calls
            (bool success, ) = payable(to).call{value: amount}("");
            require(success, "Failed to withdraw");
        }
        /// @notice claim a token from a ranged project
        /// @param voucher the voucher to use
        function claimRanged(NFTVoucher calldata voucher)
            external
            payable
            nonReentrant
            onlyProjectOfType(voucher.projectId, RANGED_PROJECT)
            onlyActiveProjects(voucher.projectId)
        {
            require(
                rangedProjects[voucher.projectId].pointer <=
                    rangedProjects[voucher.projectId].rangeEnd,
                "Project out of tokens"
            );
            _useVoucher(voucher);
            // get token id and increase pointer
            uint256 tokenId = rangedProjects[voucher.projectId].pointer;
            rangedProjects[voucher.projectId].pointer += 1;
            // transfer the NFT
            _transferToken(voucher.projectId, tokenId, msg.sender);
            if (msg.value > 0) {
                _withdraw(
                    projects[voucher.projectId].wallet,
                    msg.value,
                    voucher.projectId
                );
            }
        }
        function claimSpecifiedLazyMint(NFTVoucher calldata voucher)
            external
            payable
            nonReentrant
            onlyProjectOfType(voucher.projectId, SPECIFIED_LAZY_MINT_PROJECT)
            onlyActiveProjects(voucher.projectId)
        {
            _useVoucher(voucher);
            ISpecifiedMinter minter = ISpecifiedMinter(
                projects[voucher.projectId].nftContract
            );
            uint256 createdToken = minter.mint(msg.sender, voucher.tokenId);
            emit OnTokenClaim(
                voucher.projectId,
                msg.sender,
                createdToken,
                msg.value,
                true
            );
            if (msg.value > 0) {
                _withdraw(
                    projects[voucher.projectId].wallet,
                    msg.value,
                    voucher.projectId
                );
            }
        }
        /// @notice claim a token from a lazy mint project
        /// @param voucher the voucher to use
        function claimLazyMint(NFTVoucher calldata voucher)
            external
            payable
            nonReentrant
            onlyProjectOfType(voucher.projectId, LAZY_MINT_PROJECT)
            onlyActiveProjects(voucher.projectId)
        {
            _useVoucher(voucher);
            pendingMints[msg.sender] = projects[voucher.projectId].nftContract;
            IERC721CreatorCore erc721 = IERC721CreatorCore(
                projects[voucher.projectId].nftContract
            );
            uint256 createdToken = erc721.mintExtension(msg.sender);
            emit OnTokenClaim(
                voucher.projectId,
                msg.sender,
                createdToken,
                msg.value,
                true
            );
            if (msg.value > 0) {
                _withdraw(
                    projects[voucher.projectId].wallet,
                    msg.value,
                    voucher.projectId
                );
            }
        }
        /// @notice claim a token from a specified project
        /// @param voucher the voucher to use
        function claimSpecified(NFTVoucher calldata voucher)
            external
            payable
            nonReentrant
            onlyProjectOfType(voucher.projectId, SPECIFIED_PROJECT)
            onlyActiveProjects(voucher.projectId)
        {
            _useVoucher(voucher);
            _transferToken(voucher.projectId, voucher.tokenId, msg.sender);
            if (msg.value > 0) {
                _withdraw(
                    projects[voucher.projectId].wallet,
                    msg.value,
                    voucher.projectId
                );
            }
        }
        /// @notice claim a token from an auction project
        /// @param projectId the id of the auction
        function claimAuction(uint256 projectId)
            external
            payable
            nonReentrant
            onlyProjectOfType(projectId, AUCTION_PROJECT)
            onlyActiveProjects(projectId)
        {
            require(
                TimerLib._now() >= auctions[projectId].closeTime,
                "Auction: still running"
            );
            require(
                msg.sender == auctions[projectId].highBidder,
                "Auction: not winner"
            );
            projects[projectId].count += 1;
            _transferToken(
                projectId,
                auctions[projectId].settings.tokenId,
                msg.sender
            );
            _withdraw(
                projects[projectId].wallet,
                auctions[projectId].highBid,
                projectId
            );
        }
        /// @notice make a bid for an auction
        /// @param projectId the id of the auction
        function bidAuction(uint256 projectId)
            external
            payable
            onlyProjectOfType(projectId, AUCTION_PROJECT)
            onlyActiveProjects(projectId)
        {
            // setup the auction if it's not started yet
            if (auctions[projectId].startTime == 0) {
                _startAuction(projectId);
            } else {
                // auction needs to be running
                require(
                    TimerLib._now() < auctions[projectId].closeTime,
                    "Auction: is over"
                );
            }
            // check the bid value
            if (auctions[projectId].highBid == 0) {
                // needs to be above floor price
                require(
                    msg.value >= auctions[projectId].settings.floor,
                    "Auction: lower than floor"
                );
            } else {
                require(
                    msg.value >= _getAuctionThreshHold(projectId),
                    "Auction: lower than threshold"
                );
                // emit the event for outbid
                emit OnAuctionOutBid(
                    projectId,
                    auctions[projectId].highBidder,
                    auctions[projectId].settings.tokenId,
                    auctions[projectId].highBid
                );
            }
            // emit the event for the bid
            emit OnAuctionBid(
                projectId,
                msg.sender,
                auctions[projectId].settings.tokenId,
                msg.value
            );
            // increase the time if needed
            uint256 timeLeft = auctions[projectId].closeTime - TimerLib._now();
            if (timeLeft < auctions[projectId].settings.timeBufferSeconds) {
                auctions[projectId].closeTime +=
                    auctions[projectId].settings.timeBufferSeconds -
                    timeLeft;
            }
            // info to refund the last high bidder
            uint256 refundBid = auctions[projectId].highBid;
            address refundBidder = auctions[projectId].highBidder;
            // set the new high bidder
            auctions[projectId].highBid = msg.value;
            auctions[projectId].highBidder = msg.sender;
            // refund the last bidder
            if (refundBid > 0 && refundBidder != address(0)) {
                _withdraw(refundBidder, refundBid, projectId);
            }
        }
        /// @dev setup a project
        /// @param project the input for the project
        /// @param projectType the type of the project to create/update
        function _setProject(NftProjectInput calldata project, uint256 projectType)
            private
        {
            // check if exists, if so check if the same project type
            if (projects[project.id].timestamp != 0) {
                require(
                    projects[project.id].projectType == projectType,
                    "Wrong project type"
                );
            } else {
                // setup for new project, these cannot be edited after creation
                projects[project.id].id = project.id;
                projects[project.id].timestamp = TimerLib._now();
                projects[project.id].curator = msg.sender;
                projects[project.id].count = 0;
                projects[project.id].projectType = projectType;
                emit OnProjectCreated(project.id, msg.sender, projectType);
            }
            // general project settings
            projects[project.id].custodial = project.custodial;
            projects[project.id].wallet = project.wallet;
            projects[project.id].nftContract = project.nftContract;
            projects[project.id].paused = project.paused;
            projects[project.id].closeTime = project.closeTime;
            projects[project.id].countLimit = project.countLimit;
        }
        /// @notice Returns a hash of the given NFTVoucher, prepared using EIP712 typed data hashing rules.
        /// @param voucher An NFTVoucher to hash.
        function _hashVoucher(NFTVoucher calldata voucher)
            internal
            view
            returns (bytes32)
        {
            return
                _hashTypedDataV4(
                    keccak256(
                        abi.encode(
                            keccak256(
                                "NFTVoucher(uint256 projectId,address walletAddress,uint256 voucherId,uint256 price,uint256 validUntil,uint256 tokenId)"
                            ),
                            voucher.projectId,
                            voucher.walletAddress,
                            voucher.voucherId,
                            voucher.price,
                            voucher.validUntil,
                            voucher.tokenId
                        )
                    )
                );
        }
        /// @notice Verifies the signature for a given NFTVoucher, returning the address of the signer.
        /// @dev Will revert if the signature is invalid. Does not verify that the signer is authorized to mint NFTs.
        /// @param voucher An NFTVoucher describing an unminted NFT.
        function _recoverVoucherSigner(NFTVoucher calldata voucher)
            internal
            view
            returns (address)
        {
            // take data, hash it
            bytes32 digest = _hashVoucher(voucher);
            // take hash + signature, and get public key
            return ECDSA.recover(digest, voucher.signature);
        }
        /// @notice Transfers a token from a custodial wallet to a user wallet
        /// @param projectId the id of the related project
        /// @param tokenId the id of the token to transfer
        /// @param to the wallet who would recieve the token
        function _transferToken(
            uint256 projectId,
            uint256 tokenId,
            address to
        ) private {
            emit OnTokenClaim(projectId, msg.sender, tokenId, msg.value, false);
            IERC721 nft = IERC721(projects[projectId].nftContract);
            nft.transferFrom(projects[projectId].custodial, to, tokenId);
        }
        /// @notice approve minting for manifold contract (ERC721)
        /// @dev it is verified by setting pendingMints for a wallet address and approving only the specified wallet
        /// @param to the wallet which is expected to recieve the token
        function approveMint(
            address, /* extension */
            address to,
            uint256 /* tokenId */
        ) external virtual override {
            require(msg.sender == pendingMints[to], "Not manifold creator");
            delete pendingMints[to];
        }
        /// @notice derived from ERC165, checks support for interfaces
        /// @param interfaceId the interface id to check
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(AccessControl, IERC165)
            returns (bool)
        {
            return
                // supports open zepplin's access control
                AccessControl.supportsInterface(interfaceId) ||
                // supports maniford mint permissions (erc721)
                interfaceId == type(IERC721CreatorMintPermissions).interfaceId;
        }
        /// @notice overriding check role (from AccessControl) to treat the owner as a super user
        /// @param role the id of the role
        function _checkRole(bytes32 role) internal view virtual override {
            if (msg.sender != owner()) {
                _checkRole(role, msg.sender);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    /**
     * @title An interface for a contract that allows minting with a specified token id
     * @author Liron Navon
     * @dev This interface is used for connecting to the lazy minting contracts.
     */
    interface ISpecifiedMinter {
        function mint(address to, uint256 tokenId) external returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor() {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @author: manifold.xyz
    import "./ICreatorCore.sol";
    /**
     * @dev Core ERC721 creator interface
     */
    interface IERC721CreatorCore is ICreatorCore {
        /**
         * @dev mint a token with no extension. Can only be called by an admin.
         * Returns tokenId minted
         */
        function mintBase(address to) external returns (uint256);
        /**
         * @dev mint a token with no extension. Can only be called by an admin.
         * Returns tokenId minted
         */
        function mintBase(address to, string calldata uri) external returns (uint256);
        /**
         * @dev batch mint a token with no extension. Can only be called by an admin.
         * Returns tokenId minted
         */
        function mintBaseBatch(address to, uint16 count) external returns (uint256[] memory);
        /**
         * @dev batch mint a token with no extension. Can only be called by an admin.
         * Returns tokenId minted
         */
        function mintBaseBatch(address to, string[] calldata uris) external returns (uint256[] memory);
        /**
         * @dev mint a token. Can only be called by a registered extension.
         * Returns tokenId minted
         */
        function mintExtension(address to) external returns (uint256);
        /**
         * @dev mint a token. Can only be called by a registered extension.
         * Returns tokenId minted
         */
        function mintExtension(address to, string calldata uri) external returns (uint256);
        /**
         * @dev batch mint a token. Can only be called by a registered extension.
         * Returns tokenIds minted
         */
        function mintExtensionBatch(address to, uint16 count) external returns (uint256[] memory);
        /**
         * @dev batch mint a token. Can only be called by a registered extension.
         * Returns tokenId minted
         */
        function mintExtensionBatch(address to, string[] calldata uris) external returns (uint256[] memory);
        /**
         * @dev burn a token. Can only be called by token owner or approved address.
         * On burn, calls back to the registered extension's onBurn method
         */
        function burn(uint256 tokenId) external;
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @author: manifold.xyz
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721Creator compliant extension contracts.
     */
    interface IERC721CreatorMintPermissions is IERC165 {
        /**
         * @dev get approval to mint
         */
        function approveMint(address extension, address to, uint256 tokenId) external;
    }// SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    struct TimerData {
        /// @notice the time the contract started (seconds)
        uint256 startTime;
        /// @notice the time the contract is running from startTime (seconds)
        uint256 runningTime;
    }
    /// @title provides functionality to use time
    library TimerLib {
        using TimerLib for Timer;
        struct Timer {
            /// @notice the time the contract started
            uint256 startTime;
            /// @notice the time the contract is running from startTime
            uint256 runningTime;
            /// @notice is the timer paused
            bool paused;
        }
        /// @notice is the timer running - marked as running and has time remaining
        function _deadline(Timer storage self) internal view returns (uint256) {
            return self.startTime + self.runningTime;
        }
        function _now() internal view returns (uint256) {
            // solhint-disable-next-line not-rely-on-time
            return block.timestamp;
        }
        /// @notice is the timer running - marked as running and has time remaining
        function _isRunning(Timer storage self) internal view returns (bool) {
            return !self.paused && (self._deadline() > _now());
        }
        /// @notice starts the timer, call again to restart
        function _start(Timer storage self, uint256 runningTime) internal {
            self.paused = false;
            self.startTime = _now();
            self.runningTime = runningTime;
        }
        /// @notice updates the running time
        function _updateRunningTime(Timer storage self, uint256 runningTime)
            internal
        {
            self.runningTime = runningTime;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
    pragma solidity ^0.8.0;
    import "./ECDSA.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        address private immutable _CACHED_THIS;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _CACHED_THIS = address(this);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.3) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            } else if (error == RecoverError.InvalidSignatureV) {
                revert("ECDSA: invalid signature 'v' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                /// @solidity memory-safe-assembly
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address, RecoverError) {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            if (v != 27 && v != 28) {
                return (address(0), RecoverError.InvalidSignatureV);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `_msgSender()` is missing `role`.
         * Overriding this function changes the behavior of the {onlyRole} modifier.
         *
         * Format of the revert message is described in {_checkRole}.
         *
         * _Available since v4.6._
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(uint160(account), 20),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @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.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @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 revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * May emit a {RoleGranted} event.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        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/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // 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 v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @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: MIT
    pragma solidity ^0.8.0;
    /// @author: manifold.xyz
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /**
     * @dev Core creator interface
     */
    interface ICreatorCore is IERC165 {
        event ExtensionRegistered(address indexed extension, address indexed sender);
        event ExtensionUnregistered(address indexed extension, address indexed sender);
        event ExtensionBlacklisted(address indexed extension, address indexed sender);
        event MintPermissionsUpdated(address indexed extension, address indexed permissions, address indexed sender);
        event RoyaltiesUpdated(uint256 indexed tokenId, address payable[] receivers, uint256[] basisPoints);
        event DefaultRoyaltiesUpdated(address payable[] receivers, uint256[] basisPoints);
        event ExtensionRoyaltiesUpdated(address indexed extension, address payable[] receivers, uint256[] basisPoints);
        event ExtensionApproveTransferUpdated(address indexed extension, bool enabled);
        /**
         * @dev gets address of all extensions
         */
        function getExtensions() external view returns (address[] memory);
        /**
         * @dev add an extension.  Can only be called by contract owner or admin.
         * extension address must point to a contract implementing ICreatorExtension.
         * Returns True if newly added, False if already added.
         */
        function registerExtension(address extension, string calldata baseURI) external;
        /**
         * @dev add an extension.  Can only be called by contract owner or admin.
         * extension address must point to a contract implementing ICreatorExtension.
         * Returns True if newly added, False if already added.
         */
        function registerExtension(address extension, string calldata baseURI, bool baseURIIdentical) external;
        /**
         * @dev add an extension.  Can only be called by contract owner or admin.
         * Returns True if removed, False if already removed.
         */
        function unregisterExtension(address extension) external;
        /**
         * @dev blacklist an extension.  Can only be called by contract owner or admin.
         * This function will destroy all ability to reference the metadata of any tokens created
         * by the specified extension. It will also unregister the extension if needed.
         * Returns True if removed, False if already removed.
         */
        function blacklistExtension(address extension) external;
        /**
         * @dev set the baseTokenURI of an extension.  Can only be called by extension.
         */
        function setBaseTokenURIExtension(string calldata uri) external;
        /**
         * @dev set the baseTokenURI of an extension.  Can only be called by extension.
         * For tokens with no uri configured, tokenURI will return "uri+tokenId"
         */
        function setBaseTokenURIExtension(string calldata uri, bool identical) external;
        /**
         * @dev set the common prefix of an extension.  Can only be called by extension.
         * If configured, and a token has a uri set, tokenURI will return "prefixURI+tokenURI"
         * Useful if you want to use ipfs/arweave
         */
        function setTokenURIPrefixExtension(string calldata prefix) external;
        /**
         * @dev set the tokenURI of a token extension.  Can only be called by extension that minted token.
         */
        function setTokenURIExtension(uint256 tokenId, string calldata uri) external;
        /**
         * @dev set the tokenURI of a token extension for multiple tokens.  Can only be called by extension that minted token.
         */
        function setTokenURIExtension(uint256[] memory tokenId, string[] calldata uri) external;
        /**
         * @dev set the baseTokenURI for tokens with no extension.  Can only be called by owner/admin.
         * For tokens with no uri configured, tokenURI will return "uri+tokenId"
         */
        function setBaseTokenURI(string calldata uri) external;
        /**
         * @dev set the common prefix for tokens with no extension.  Can only be called by owner/admin.
         * If configured, and a token has a uri set, tokenURI will return "prefixURI+tokenURI"
         * Useful if you want to use ipfs/arweave
         */
        function setTokenURIPrefix(string calldata prefix) external;
        /**
         * @dev set the tokenURI of a token with no extension.  Can only be called by owner/admin.
         */
        function setTokenURI(uint256 tokenId, string calldata uri) external;
        /**
         * @dev set the tokenURI of multiple tokens with no extension.  Can only be called by owner/admin.
         */
        function setTokenURI(uint256[] memory tokenIds, string[] calldata uris) external;
        /**
         * @dev set a permissions contract for an extension.  Used to control minting.
         */
        function setMintPermissions(address extension, address permissions) external;
        /**
         * @dev Configure so transfers of tokens created by the caller (must be extension) gets approval
         * from the extension before transferring
         */
        function setApproveTransferExtension(bool enabled) external;
        /**
         * @dev get the extension of a given token
         */
        function tokenExtension(uint256 tokenId) external view returns (address);
        /**
         * @dev Set default royalties
         */
        function setRoyalties(address payable[] calldata receivers, uint256[] calldata basisPoints) external;
        /**
         * @dev Set royalties of a token
         */
        function setRoyalties(uint256 tokenId, address payable[] calldata receivers, uint256[] calldata basisPoints) external;
        /**
         * @dev Set royalties of an extension
         */
        function setRoyaltiesExtension(address extension, address payable[] calldata receivers, uint256[] calldata basisPoints) external;
        /**
         * @dev Get royalites of a token.  Returns list of receivers and basisPoints
         */
        function getRoyalties(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
        
        // Royalty support for various other standards
        function getFeeRecipients(uint256 tokenId) external view returns (address payable[] memory);
        function getFeeBps(uint256 tokenId) external view returns (uint[] memory);
        function getFees(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
        function royaltyInfo(uint256 tokenId, uint256 value) external view returns (address, uint256);
    }
    

    File 2 of 2: Ringers962Edition
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    import "../implementations/Erc1155/LazyMintByTokenIdERC1155.sol";
    /**
     * @title A Lazy minting contract for Ringers #962: Edition by Dmitri Cherniak
     * @author Liron Navon
     */
    contract Ringers962Edition is LazyMintByTokenIdERC1155 {
        constructor(
            string memory _name,
            address _minter,
            string memory _uri,
            address royaltiesReciever,
            uint256 royaltiesFraction
        )
            LazyMintByTokenIdERC1155(
                _name,
                _minter,
                _uri,
                royaltiesReciever,
                royaltiesFraction
            )
        {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import {ISpecifiedMinter} from "../../libraries/ISpecifiedMinter.sol";
    import {SimpleRoyalties} from "../../libraries/SimpleRoyalties.sol";
    /**
     * @title A Lazy minting contract that can mint arbitrary token ids
     * @author Liron Navon
     * @notice this contract specified an address "minter" which is the only address that can call "mint"
     */
    contract LazyMintByTokenIdERC1155 is
        ERC1155,
        SimpleRoyalties,
        Ownable,
        ISpecifiedMinter
    {
        /// @dev only the minter address can call "mint"
        address public minter;
        string public name;
        constructor(
            string memory _name,
            address _minter,
            string memory _uri,
            address royaltiesReciever,
            uint256 royaltiesFraction
        ) ERC1155(_uri) SimpleRoyalties(royaltiesReciever, royaltiesFraction) {
            minter = _minter;
            name = _name;
        }
        modifier onlyMinter() {
            require(minter == msg.sender, "Unauthorized minter");
            _;
        }
        /**
         * @dev Mints a token to a given user
         */
        function _mintToken(address to, uint256 tokenId) private returns (uint256) {
            _mint(to, tokenId, 1, "");
            return tokenId;
        }
        /**
         * @dev Calls mint, only for requests of the minter address
         */
        function mint(
            address to,
            uint256 tokenId
        ) public onlyMinter returns (uint256) {
            return _mintToken(to, tokenId);
        }
        /**
         * @dev Calls mint, only for the contract owner
         */
        function ownerMint(
            address to,
            uint256 tokenId
        ) public onlyOwner returns (uint256) {
            return _mintToken(to, tokenId);
        }
        /**
         * @dev Set a new uri
         */
        function setUri(string calldata _uri) public onlyOwner {
            _setURI(_uri);
        }
        /**
         * @dev Set a new minter
         */
        function setMinter(address _minter) public onlyOwner {
            minter = _minter;
        }
        /**
         * @dev Set new royalties for the contract
         */
        function setRoyalties(address reciever, uint256 fraction) public onlyOwner {
            _setRoyalty(reciever, fraction);
        }
        function supportsInterface(
            bytes4 interfaceId
        ) public view virtual override(SimpleRoyalties, ERC1155) returns (bool) {
            return super.supportsInterface(interfaceId);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    import "@openzeppelin/contracts/interfaces/IERC2981.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
     * @author Liron Navon
     *
     * Royalty information can only be specified globally for all token ids via {_setDefaultRoyalty}.
     *
     * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
     * fee is specified in basis points by default.
     *
     * It does the same as open zepplins royalties setup,
     * but doesn't include a royalty per token, only a single royalty for all tokens.
     * https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721Royalty
     */
    contract SimpleRoyalties is IERC2981, ERC165 {
        struct RoyaltyInfo {
            address receiver;
            uint96 royaltyFraction;
        }
        address private _royaltyReciever;
        uint256 private _royaltyFraction;
        constructor(address _reciever, uint256 _fraction) {
            _setRoyalty(_reciever, _fraction);
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(
            bytes4 interfaceId
        ) public view virtual override(IERC165, ERC165) returns (bool) {
            return
                interfaceId == type(IERC2981).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @inheritdoc IERC2981
         */
        function royaltyInfo(
            uint256,
            uint256 _salePrice
        ) public view virtual override returns (address, uint256) {
            uint256 royaltyAmount = (_salePrice * _royaltyFraction) /
                _feeDenominator();
            return (_royaltyReciever, royaltyAmount);
        }
        /**
         * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
         * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
         * override.
         */
        function _feeDenominator() internal pure virtual returns (uint256) {
            return 10000;
        }
        /**
         * @dev Sets the royalty information that all ids in this contract will default to.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setRoyalty(address receiver, uint256 fraction) internal virtual {
            require(fraction <= _feeDenominator(), "ERC2981: fraction too high");
            require(receiver != address(0), "ERC2981: invalid receiver");
            _royaltyReciever = receiver;
            _royaltyFraction = fraction;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    /*----------------------------------------------------------\\
    |                             _                 _           |
    |        /\\                  | |     /\\        | |          |
    |       /  \\__   ____ _ _ __ | |_   /  \\   _ __| |_ ___     |
    |      / /\\ \\ \\ / / _` | '_ \\| __| / /\\ \\ | '__| __/ _ \\    |
    |     / ____ \\ V / (_| | | | | |_ / ____ \\| |  | ||  __/    |
    |    /_/    \\_\\_/ \\__,_|_| |_|\\__/_/    \\_\\_|   \\__\\___|    |
    |                                                           |
    |    https://avantarte.com/careers                          |
    |    https://avantarte.com/support/contact                  |
    |                                                           |
    \\----------------------------------------------------------*/
    /**
     * @title An interface for a contract that allows minting with a specified token id
     * @author Liron Navon
     * @dev This interface is used for connecting to the lazy minting contracts.
     */
    interface ISpecifiedMinter {
        function mint(address to, uint256 tokenId) external returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        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 = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            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);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol)
    pragma solidity ^0.8.0;
    import "./IERC1155.sol";
    import "./IERC1155Receiver.sol";
    import "./extensions/IERC1155MetadataURI.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/introspection/ERC165.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 ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using Address 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}.
         */
        constructor(string memory uri_) {
            _setURI(uri_);
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) 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 IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.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 IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.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;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/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 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.8.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        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) {
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1);
                ///////////////////////////////////////////////
                // 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 10, 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 * 8) < value ? 1 : 0);
            }
        }
    }
    // 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.8.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 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 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
    pragma solidity ^0.8.0;
    import "../IERC1155.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 IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\\{id\\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.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 IERC1155 is IERC165 {
        /**
         * @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 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }