// File: @chainlink/contracts/src/v0.8/ConfirmedOwner.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ConfirmedOwnerWithProposal.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
  constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}

// File: @chainlink/contracts/src/v0.8/ConfirmedOwnerWithProposal.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/OwnableInterface.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwnerWithProposal is OwnableInterface {
  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    require(newOwner != address(0), "Cannot set owner to zero");

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /**
   * @notice Allows an owner to begin transferring ownership to a new address,
   * pending.
   */
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /**
   * @notice Allows an ownership transfer to be completed by the recipient.
   */
  function acceptOwnership() external override {
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /**
   * @notice Get the current owner
   */
  function owner() public view override returns (address) {
    return s_owner;
  }

  /**
   * @notice validate, transfer ownership, and emit relevant events
   */
  function _transferOwnership(address to) private {
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /**
   * @notice validate access
   */
  function _validateOwnership() internal view {
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /**
   * @notice Reverts if called by anyone other than the contract owner.
   */
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}

// File: @chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/** ****************************************************************************
 * @notice Interface for contracts using VRF randomness
 * *****************************************************************************
 * @dev PURPOSE
 *
 * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
 * @dev to Vera the verifier in such a way that Vera can be sure he's not
 * @dev making his output up to suit himself. Reggie provides Vera a public key
 * @dev to which he knows the secret key. Each time Vera provides a seed to
 * @dev Reggie, he gives back a value which is computed completely
 * @dev deterministically from the seed and the secret key.
 *
 * @dev Reggie provides a proof by which Vera can verify that the output was
 * @dev correctly computed once Reggie tells it to her, but without that proof,
 * @dev the output is indistinguishable to her from a uniform random sample
 * @dev from the output space.
 *
 * @dev The purpose of this contract is to make it easy for unrelated contracts
 * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
 * @dev simple access to a verifiable source of randomness. It ensures 2 things:
 * @dev 1. The fulfillment came from the VRFCoordinator
 * @dev 2. The consumer contract implements fulfillRandomWords.
 * *****************************************************************************
 * @dev USAGE
 *
 * @dev Calling contracts must inherit from VRFConsumerBase, and can
 * @dev initialize VRFConsumerBase's attributes in their constructor as
 * @dev shown:
 *
 * @dev   contract VRFConsumer {
 * @dev     constructor(<other arguments>, address _vrfCoordinator, address _link)
 * @dev       VRFConsumerBase(_vrfCoordinator) public {
 * @dev         <initialization with other arguments goes here>
 * @dev       }
 * @dev   }
 *
 * @dev The oracle will have given you an ID for the VRF keypair they have
 * @dev committed to (let's call it keyHash). Create subscription, fund it
 * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
 * @dev subscription management functions).
 * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
 * @dev callbackGasLimit, numWords),
 * @dev see (VRFCoordinatorInterface for a description of the arguments).
 *
 * @dev Once the VRFCoordinator has received and validated the oracle's response
 * @dev to your request, it will call your contract's fulfillRandomWords method.
 *
 * @dev The randomness argument to fulfillRandomWords is a set of random words
 * @dev generated from your requestId and the blockHash of the request.
 *
 * @dev If your contract could have concurrent requests open, you can use the
 * @dev requestId returned from requestRandomWords to track which response is associated
 * @dev with which randomness request.
 * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
 * @dev if your contract could have multiple requests in flight simultaneously.
 *
 * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
 * @dev differ.
 *
 * *****************************************************************************
 * @dev SECURITY CONSIDERATIONS
 *
 * @dev A method with the ability to call your fulfillRandomness method directly
 * @dev could spoof a VRF response with any random value, so it's critical that
 * @dev it cannot be directly called by anything other than this base contract
 * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
 *
 * @dev For your users to trust that your contract's random behavior is free
 * @dev from malicious interference, it's best if you can write it so that all
 * @dev behaviors implied by a VRF response are executed *during* your
 * @dev fulfillRandomness method. If your contract must store the response (or
 * @dev anything derived from it) and use it later, you must ensure that any
 * @dev user-significant behavior which depends on that stored value cannot be
 * @dev manipulated by a subsequent VRF request.
 *
 * @dev Similarly, both miners and the VRF oracle itself have some influence
 * @dev over the order in which VRF responses appear on the blockchain, so if
 * @dev your contract could have multiple VRF requests in flight simultaneously,
 * @dev you must ensure that the order in which the VRF responses arrive cannot
 * @dev be used to manipulate your contract's user-significant behavior.
 *
 * @dev Since the block hash of the block which contains the requestRandomness
 * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
 * @dev miner could, in principle, fork the blockchain to evict the block
 * @dev containing the request, forcing the request to be included in a
 * @dev different block with a different hash, and therefore a different input
 * @dev to the VRF. However, such an attack would incur a substantial economic
 * @dev cost. This cost scales with the number of blocks the VRF oracle waits
 * @dev until it calls responds to a request. It is for this reason that
 * @dev that you can signal to an oracle you'd like them to wait longer before
 * @dev responding to the request (however this is not enforced in the contract
 * @dev and so remains effective only in the case of unmodified oracle software).
 */
abstract contract VRFConsumerBaseV2 {
  error OnlyCoordinatorCanFulfill(address have, address want);
  address private immutable vrfCoordinator;

  /**
   * @param _vrfCoordinator address of VRFCoordinator contract
   */
  constructor(address _vrfCoordinator) {
    vrfCoordinator = _vrfCoordinator;
  }

  /**
   * @notice fulfillRandomness handles the VRF response. Your contract must
   * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
   * @notice principles to keep in mind when implementing your fulfillRandomness
   * @notice method.
   *
   * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
   * @dev signature, and will call it once it has verified the proof
   * @dev associated with the randomness. (It is triggered via a call to
   * @dev rawFulfillRandomness, below.)
   *
   * @param requestId The Id initially returned by requestRandomness
   * @param randomWords the VRF output expanded to the requested number of words
   */
  function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;

  // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
  // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
  // the origin of the call
  function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
    if (msg.sender != vrfCoordinator) {
      revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
    }
    fulfillRandomWords(requestId, randomWords);
  }
}

// File: @chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

  function symbol() external view returns (string memory tokenSymbol);

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function transferFrom(
    address from,
    address to,
    uint256 value
  ) external returns (bool success);
}

// File: @chainlink/contracts/src/v0.8/interfaces/OwnableInterface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface OwnableInterface {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

// File: @chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface VRFCoordinatorV2Interface {
  /**
   * @notice Get configuration relevant for making requests
   * @return minimumRequestConfirmations global min for request confirmations
   * @return maxGasLimit global max for request gas limit
   * @return s_provingKeyHashes list of registered key hashes
   */
  function getRequestConfig()
    external
    view
    returns (
      uint16,
      uint32,
      bytes32[] memory
    );

  /**
   * @notice Request a set of random words.
   * @param keyHash - Corresponds to a particular oracle job which uses
   * that key for generating the VRF proof. Different keyHash's have different gas price
   * ceilings, so you can select a specific one to bound your maximum per request cost.
   * @param subId  - The ID of the VRF subscription. Must be funded
   * with the minimum subscription balance required for the selected keyHash.
   * @param minimumRequestConfirmations - How many blocks you'd like the
   * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
   * for why you may want to request more. The acceptable range is
   * [minimumRequestBlockConfirmations, 200].
   * @param callbackGasLimit - How much gas you'd like to receive in your
   * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
   * may be slightly less than this amount because of gas used calling the function
   * (argument decoding etc.), so you may need to request slightly more than you expect
   * to have inside fulfillRandomWords. The acceptable range is
   * [0, maxGasLimit]
   * @param numWords - The number of uint256 random values you'd like to receive
   * in your fulfillRandomWords callback. Note these numbers are expanded in a
   * secure way by the VRFCoordinator from a single random value supplied by the oracle.
   * @return requestId - A unique identifier of the request. Can be used to match
   * a request to a response in fulfillRandomWords.
   */
  function requestRandomWords(
    bytes32 keyHash,
    uint64 subId,
    uint16 minimumRequestConfirmations,
    uint32 callbackGasLimit,
    uint32 numWords
  ) external returns (uint256 requestId);

  /**
   * @notice Create a VRF subscription.
   * @return subId - A unique subscription id.
   * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
   * @dev Note to fund the subscription, use transferAndCall. For example
   * @dev  LINKTOKEN.transferAndCall(
   * @dev    address(COORDINATOR),
   * @dev    amount,
   * @dev    abi.encode(subId));
   */
  function createSubscription() external returns (uint64 subId);

  /**
   * @notice Get a VRF subscription.
   * @param subId - ID of the subscription
   * @return balance - LINK balance of the subscription in juels.
   * @return reqCount - number of requests for this subscription, determines fee tier.
   * @return owner - owner of the subscription.
   * @return consumers - list of consumer address which are able to use this subscription.
   */
  function getSubscription(uint64 subId)
    external
    view
    returns (
      uint96 balance,
      uint64 reqCount,
      address owner,
      address[] memory consumers
    );

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @param newOwner - proposed new owner of the subscription
   */
  function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @dev will revert if original owner of subId has
   * not requested that msg.sender become the new owner.
   */
  function acceptSubscriptionOwnerTransfer(uint64 subId) external;

  /**
   * @notice Add a consumer to a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - New consumer which can use the subscription
   */
  function addConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Remove a consumer from a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - Consumer to remove from the subscription
   */
  function removeConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Cancel a subscription
   * @param subId - ID of the subscription
   * @param to - Where to send the remaining LINK to
   */
  function cancelSubscription(uint64 subId, address to) external;

  /*
   * @notice Check to see if there exists a request commitment consumers
   * for all consumers and keyhashes for a given sub.
   * @param subId - ID of the subscription
   * @return true if there exists at least one unfulfilled request for the subscription, false
   * otherwise.
   */
  function pendingRequestExists(uint64 subId) external view returns (bool);
}

// File: @manifoldxyz/creator-core-solidity/contracts/ERC721Creator.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

import "@manifoldxyz/libraries-solidity/contracts/access/AdminControl.sol";
import "./core/ERC721CreatorCore.sol";

/**
 * @dev ERC721Creator implementation
 */
contract ERC721Creator is AdminControl, ERC721, ERC721CreatorCore {

    constructor (string memory _name, string memory _symbol) ERC721(_name, _symbol) {
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721CreatorCore, AdminControl) returns (bool) {
        return ERC721CreatorCore.supportsInterface(interfaceId) || ERC721.supportsInterface(interfaceId) || AdminControl.supportsInterface(interfaceId);
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
        _approveTransfer(from, to, tokenId);    
    }

    /**
     * @dev See {ICreatorCore-registerExtension}.
     */
    function registerExtension(address extension, string calldata baseURI) external override adminRequired {
        requireNonBlacklist(extension);
        _registerExtension(extension, baseURI, false);
    }

    /**
     * @dev See {ICreatorCore-registerExtension}.
     */
    function registerExtension(address extension, string calldata baseURI, bool baseURIIdentical) external override adminRequired {
        requireNonBlacklist(extension);
        _registerExtension(extension, baseURI, baseURIIdentical);
    }


    /**
     * @dev See {ICreatorCore-unregisterExtension}.
     */
    function unregisterExtension(address extension) external override adminRequired {
        _unregisterExtension(extension);
    }

    /**
     * @dev See {ICreatorCore-blacklistExtension}.
     */
    function blacklistExtension(address extension) external override adminRequired {
        _blacklistExtension(extension);
    }

    /**
     * @dev See {ICreatorCore-setBaseTokenURIExtension}.
     */
    function setBaseTokenURIExtension(string calldata uri) external override {
        requireExtension();
        _setBaseTokenURIExtension(uri, false);
    }

    /**
     * @dev See {ICreatorCore-setBaseTokenURIExtension}.
     */
    function setBaseTokenURIExtension(string calldata uri, bool identical) external override {
        requireExtension();
        _setBaseTokenURIExtension(uri, identical);
    }

    /**
     * @dev See {ICreatorCore-setTokenURIPrefixExtension}.
     */
    function setTokenURIPrefixExtension(string calldata prefix) external override {
        requireExtension();
        _setTokenURIPrefixExtension(prefix);
    }

    /**
     * @dev See {ICreatorCore-setTokenURIExtension}.
     */
    function setTokenURIExtension(uint256 tokenId, string calldata uri) external override {
        requireExtension();
        _setTokenURIExtension(tokenId, uri);
    }

    /**
     * @dev See {ICreatorCore-setTokenURIExtension}.
     */
    function setTokenURIExtension(uint256[] memory tokenIds, string[] calldata uris) external override {
        requireExtension();
        require(tokenIds.length == uris.length, "Invalid input");
        for (uint i; i < tokenIds.length;) {
            _setTokenURIExtension(tokenIds[i], uris[i]);
            unchecked { ++i; }
        }
    }

    /**
     * @dev See {ICreatorCore-setBaseTokenURI}.
     */
    function setBaseTokenURI(string calldata uri) external override adminRequired {
        _setBaseTokenURI(uri);
    }

    /**
     * @dev See {ICreatorCore-setTokenURIPrefix}.
     */
    function setTokenURIPrefix(string calldata prefix) external override adminRequired {
        _setTokenURIPrefix(prefix);
    }

    /**
     * @dev See {ICreatorCore-setTokenURI}.
     */
    function setTokenURI(uint256 tokenId, string calldata uri) external override adminRequired {
        _setTokenURI(tokenId, uri);
    }

    /**
     * @dev See {ICreatorCore-setTokenURI}.
     */
    function setTokenURI(uint256[] memory tokenIds, string[] calldata uris) external override adminRequired {
        require(tokenIds.length == uris.length, "Invalid input");
        for (uint i; i < tokenIds.length;) {
            _setTokenURI(tokenIds[i], uris[i]);
            unchecked { ++i; }
        }
    }

    /**
     * @dev See {ICreatorCore-setMintPermissions}.
     */
    function setMintPermissions(address extension, address permissions) external override adminRequired {
        _setMintPermissions(extension, permissions);
    }

    /**
     * @dev See {IERC721CreatorCore-mintBase}.
     */
    function mintBase(address to) public virtual override nonReentrant adminRequired returns(uint256) {
        return _mintBase(to, "");
    }

    /**
     * @dev See {IERC721CreatorCore-mintBase}.
     */
    function mintBase(address to, string calldata uri) public virtual override nonReentrant adminRequired returns(uint256) {
        return _mintBase(to, uri);
    }

    /**
     * @dev See {IERC721CreatorCore-mintBaseBatch}.
     */
    function mintBaseBatch(address to, uint16 count) public virtual override nonReentrant adminRequired returns(uint256[] memory tokenIds) {
        tokenIds = new uint256[](count);
        for (uint16 i = 0; i < count;) {
            tokenIds[i] = _mintBase(to, "");
            unchecked { ++i; }
        }
    }

    /**
     * @dev See {IERC721CreatorCore-mintBaseBatch}.
     */
    function mintBaseBatch(address to, string[] calldata uris) public virtual override nonReentrant adminRequired returns(uint256[] memory tokenIds) {
        tokenIds = new uint256[](uris.length);
        for (uint i; i < uris.length;) {
            tokenIds[i] = _mintBase(to, uris[i]);
            unchecked { ++i; }
        }
    }

    /**
     * @dev Mint token with no extension
     */
    function _mintBase(address to, string memory uri) internal virtual returns(uint256 tokenId) {
        ++_tokenCount;
        tokenId = _tokenCount;

        _safeMint(to, tokenId);

        if (bytes(uri).length > 0) {
            _tokenURIs[tokenId] = uri;
        }

        // Call post mint
        _postMintBase(to, tokenId);
    }


    /**
     * @dev See {IERC721CreatorCore-mintExtension}.
     */
    function mintExtension(address to) public virtual override nonReentrant returns(uint256) {
        requireExtension();
        return _mintExtension(to, "");
    }

    /**
     * @dev See {IERC721CreatorCore-mintExtension}.
     */
    function mintExtension(address to, string calldata uri) public virtual override nonReentrant returns(uint256) {
        requireExtension();
        return _mintExtension(to, uri);
    }

    /**
     * @dev See {IERC721CreatorCore-mintExtensionBatch}.
     */
    function mintExtensionBatch(address to, uint16 count) public virtual override nonReentrant returns(uint256[] memory tokenIds) {
        requireExtension();
        tokenIds = new uint256[](count);
        for (uint16 i = 0; i < count;) {
            tokenIds[i] = _mintExtension(to, "");
            unchecked { ++i; }
        }
    }

    /**
     * @dev See {IERC721CreatorCore-mintExtensionBatch}.
     */
    function mintExtensionBatch(address to, string[] calldata uris) public virtual override nonReentrant returns(uint256[] memory tokenIds) {
        requireExtension();
        tokenIds = new uint256[](uris.length);
        for (uint i; i < uris.length;) {
            tokenIds[i] = _mintExtension(to, uris[i]);
            unchecked { ++i; }
        }
    }
    
    /**
     * @dev Mint token via extension
     */
    function _mintExtension(address to, string memory uri) internal virtual returns(uint256 tokenId) {
        ++_tokenCount;
        tokenId = _tokenCount;

        _checkMintPermissions(to, tokenId);

        // Track the extension that minted the token
        _tokensExtension[tokenId] = msg.sender;

        _safeMint(to, tokenId);

        if (bytes(uri).length > 0) {
            _tokenURIs[tokenId] = uri;
        }
        
        // Call post mint
        _postMintExtension(to, tokenId);
    }

    /**
     * @dev See {IERC721CreatorCore-tokenExtension}.
     */
    function tokenExtension(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "Nonexistent token");
        return _tokenExtension(tokenId);
    }

    /**
     * @dev See {IERC721CreatorCore-burn}.
     */
    function burn(uint256 tokenId) public virtual override nonReentrant {
        require(_isApprovedOrOwner(msg.sender, tokenId), "Caller is not owner nor approved");
        address owner = ownerOf(tokenId);
        _burn(tokenId);
        _postBurn(owner, tokenId);
    }

    /**
     * @dev See {ICreatorCore-setRoyalties}.
     */
    function setRoyalties(address payable[] calldata receivers, uint256[] calldata basisPoints) external override adminRequired {
        _setRoyaltiesExtension(address(0), receivers, basisPoints);
    }

    /**
     * @dev See {ICreatorCore-setRoyalties}.
     */
    function setRoyalties(uint256 tokenId, address payable[] calldata receivers, uint256[] calldata basisPoints) external override adminRequired {
        require(_exists(tokenId), "Nonexistent token");
        _setRoyalties(tokenId, receivers, basisPoints);
    }

    /**
     * @dev See {ICreatorCore-setRoyaltiesExtension}.
     */
    function setRoyaltiesExtension(address extension, address payable[] calldata receivers, uint256[] calldata basisPoints) external override adminRequired {
        _setRoyaltiesExtension(extension, receivers, basisPoints);
    }

    /**
     * @dev See {ICreatorCore-getRoyalties}.
     */
    function getRoyalties(uint256 tokenId) external view virtual override returns (address payable[] memory, uint256[] memory) {
        require(_exists(tokenId), "Nonexistent token");
        return _getRoyalties(tokenId);
    }

    /**
     * @dev See {ICreatorCore-getFees}.
     */
    function getFees(uint256 tokenId) external view virtual override returns (address payable[] memory, uint256[] memory) {
        require(_exists(tokenId), "Nonexistent token");
        return _getRoyalties(tokenId);
    }

    /**
     * @dev See {ICreatorCore-getFeeRecipients}.
     */
    function getFeeRecipients(uint256 tokenId) external view virtual override returns (address payable[] memory) {
        require(_exists(tokenId), "Nonexistent token");
        return _getRoyaltyReceivers(tokenId);
    }

    /**
     * @dev See {ICreatorCore-getFeeBps}.
     */
    function getFeeBps(uint256 tokenId) external view virtual override returns (uint[] memory) {
        require(_exists(tokenId), "Nonexistent token");
        return _getRoyaltyBPS(tokenId);
    }
    
    /**
     * @dev See {ICreatorCore-royaltyInfo}.
     */
    function royaltyInfo(uint256 tokenId, uint256 value) external view virtual override returns (address, uint256) {
        require(_exists(tokenId), "Nonexistent token");
        return _getRoyaltyInfo(tokenId, value);
    } 

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "Nonexistent token");
        return _tokenURI(tokenId);
    }

    /**
     * @dev See {ICreatorCore-setApproveTransfer}.
     */
    function setApproveTransfer(address extension) external override adminRequired {
        _setApproveTransferBase(extension);
    }
}
// File: @manifoldxyz/creator-core-solidity/contracts/core/CreatorCore.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

import "../extensions/ICreatorExtensionTokenURI.sol";
import "../extensions/ICreatorExtensionRoyalties.sol";

import "./ICreatorCore.sol";

/**
 * @dev Core creator implementation
 */
abstract contract CreatorCore is ReentrancyGuard, ICreatorCore, ERC165 {
    using Strings for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;
    using AddressUpgradeable for address;

    uint256 internal _tokenCount = 0;

    // Base approve transfers address location
    address internal _approveTransferBase;

    // Track registered extensions data
    EnumerableSet.AddressSet internal _extensions;
    EnumerableSet.AddressSet internal _blacklistedExtensions;
    mapping (address => address) internal _extensionPermissions;
    mapping (address => bool) internal _extensionApproveTransfers;
    
    // For tracking which extension a token was minted by
    mapping (uint256 => address) internal _tokensExtension;

    // The baseURI for a given extension
    mapping (address => string) private _extensionBaseURI;
    mapping (address => bool) private _extensionBaseURIIdentical;

    // The prefix for any tokens with a uri configured
    mapping (address => string) private _extensionURIPrefix;

    // Mapping for individual token URIs
    mapping (uint256 => string) internal _tokenURIs;

    // Royalty configurations
    struct RoyaltyConfig {
        address payable receiver;
        uint16 bps;
    }
    mapping (address => RoyaltyConfig[]) internal _extensionRoyalty;
    mapping (uint256 => RoyaltyConfig[]) internal _tokenRoyalty;

    bytes4 private constant _CREATOR_CORE_V1 = 0x28f10a21;

    /**
     * External interface identifiers for royalties
     */

    /**
     *  @dev CreatorCore
     *
     *  bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6
     *
     *  => 0xbb3bafd6 = 0xbb3bafd6
     */
    bytes4 private constant _INTERFACE_ID_ROYALTIES_CREATORCORE = 0xbb3bafd6;

    /**
     *  @dev Rarible: RoyaltiesV1
     *
     *  bytes4(keccak256('getFeeRecipients(uint256)')) == 0xb9c4d9fb
     *  bytes4(keccak256('getFeeBps(uint256)')) == 0x0ebd4c7f
     *
     *  => 0xb9c4d9fb ^ 0x0ebd4c7f = 0xb7799584
     */
    bytes4 private constant _INTERFACE_ID_ROYALTIES_RARIBLE = 0xb7799584;

    /**
     *  @dev Foundation
     *
     *  bytes4(keccak256('getFees(uint256)')) == 0xd5a06d4c
     *
     *  => 0xd5a06d4c = 0xd5a06d4c
     */
    bytes4 private constant _INTERFACE_ID_ROYALTIES_FOUNDATION = 0xd5a06d4c;

    /**
     *  @dev EIP-2981
     *
     * bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
     *
     * => 0x2a55205a = 0x2a55205a
     */
    bytes4 private constant _INTERFACE_ID_ROYALTIES_EIP2981 = 0x2a55205a;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(ICreatorCore).interfaceId || interfaceId == _CREATOR_CORE_V1 || super.supportsInterface(interfaceId)
            || interfaceId == _INTERFACE_ID_ROYALTIES_CREATORCORE || interfaceId == _INTERFACE_ID_ROYALTIES_RARIBLE
            || interfaceId == _INTERFACE_ID_ROYALTIES_FOUNDATION || interfaceId == _INTERFACE_ID_ROYALTIES_EIP2981;
    }

    /**
     * @dev Only allows registered extensions to call the specified function
     */
    function requireExtension() internal view {
        require(_extensions.contains(msg.sender), "Must be registered extension");
    }

    /**
     * @dev Only allows non-blacklisted extensions
     */
    function requireNonBlacklist(address extension) internal view {
        require(!_blacklistedExtensions.contains(extension), "Extension blacklisted");
    }   

    /**
     * @dev See {ICreatorCore-getExtensions}.
     */
    function getExtensions() external view override returns (address[] memory extensions) {
        extensions = new address[](_extensions.length());
        for (uint i; i < _extensions.length();) {
            extensions[i] = _extensions.at(i);
            unchecked { ++i; }
        }
        return extensions;
    }

    /**
     * @dev Register an extension
     */
    function _registerExtension(address extension, string calldata baseURI, bool baseURIIdentical) internal {
        require(extension != address(this) && extension.isContract(), "Invalid");
        emit ExtensionRegistered(extension, msg.sender);
        _extensionBaseURI[extension] = baseURI;
        _extensionBaseURIIdentical[extension] = baseURIIdentical;
        _extensions.add(extension);
        _setApproveTransferExtension(extension, true);
    }

    /**
     * @dev See {ICreatorCore-setApproveTransferExtension}.
     */
    function setApproveTransferExtension(bool enabled) external override {
        requireExtension();
        _setApproveTransferExtension(msg.sender, enabled);
    }

    /**
     * @dev Set whether or not tokens minted by the extension defers transfer approvals to the extension
     */
    function _setApproveTransferExtension(address extension, bool enabled) internal virtual;

    /**
     * @dev Unregister an extension
     */
    function _unregisterExtension(address extension) internal {
        emit ExtensionUnregistered(extension, msg.sender);
        _extensions.remove(extension);
    }

    /**
     * @dev Blacklist an extension
     */
    function _blacklistExtension(address extension) internal {
       require(extension != address(0) && extension != address(this), "Cannot blacklist yourself");
       if (_extensions.contains(extension)) {
           emit ExtensionUnregistered(extension, msg.sender);
           _extensions.remove(extension);
       }
       if (!_blacklistedExtensions.contains(extension)) {
           emit ExtensionBlacklisted(extension, msg.sender);
           _blacklistedExtensions.add(extension);
       }
    }

    /**
     * @dev Set base token uri for an extension
     */
    function _setBaseTokenURIExtension(string calldata uri, bool identical) internal {
        _extensionBaseURI[msg.sender] = uri;
        _extensionBaseURIIdentical[msg.sender] = identical;
    }

    /**
     * @dev Set token uri prefix for an extension
     */
    function _setTokenURIPrefixExtension(string calldata prefix) internal {
        _extensionURIPrefix[msg.sender] = prefix;
    }

    /**
     * @dev Set token uri for a token of an extension
     */
    function _setTokenURIExtension(uint256 tokenId, string calldata uri) internal {
        require(_tokensExtension[tokenId] == msg.sender, "Invalid token");
        _tokenURIs[tokenId] = uri;
    }

    /**
     * @dev Set base token uri for tokens with no extension
     */
    function _setBaseTokenURI(string memory uri) internal {
        _extensionBaseURI[address(0)] = uri;
    }

    /**
     * @dev Set token uri prefix for tokens with no extension
     */
    function _setTokenURIPrefix(string calldata prefix) internal {
        _extensionURIPrefix[address(0)] = prefix;
    }


    /**
     * @dev Set token uri for a token with no extension
     */
    function _setTokenURI(uint256 tokenId, string calldata uri) internal {
        require(tokenId > 0 && tokenId <= _tokenCount && _tokensExtension[tokenId] == address(0), "Invalid token");
        _tokenURIs[tokenId] = uri;
    }

    /**
     * @dev Retrieve a token's URI
     */
    function _tokenURI(uint256 tokenId) internal view returns (string memory) {
        require(tokenId > 0 && tokenId <= _tokenCount, "Invalid token");

        address extension = _tokensExtension[tokenId];
        require(!_blacklistedExtensions.contains(extension), "Extension blacklisted");

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            if (bytes(_extensionURIPrefix[extension]).length != 0) {
                return string(abi.encodePacked(_extensionURIPrefix[extension],_tokenURIs[tokenId]));
            }
            return _tokenURIs[tokenId];
        }

        if (ERC165Checker.supportsInterface(extension, type(ICreatorExtensionTokenURI).interfaceId)) {
            return ICreatorExtensionTokenURI(extension).tokenURI(address(this), tokenId);
        }

        if (!_extensionBaseURIIdentical[extension]) {
            return string(abi.encodePacked(_extensionBaseURI[extension], tokenId.toString()));
        } else {
            return _extensionBaseURI[extension];
        }
    }

    /**
     * Get token extension
     */
    function _tokenExtension(uint256 tokenId) internal view returns (address extension) {
        extension = _tokensExtension[tokenId];

        require(extension != address(0), "No extension for token");
        require(!_blacklistedExtensions.contains(extension), "Extension blacklisted");

        return extension;
    }

    /**
     * Helper to get royalties for a token
     */
    function _getRoyalties(uint256 tokenId) view internal returns (address payable[] memory receivers, uint256[] memory bps) {

        // Get token level royalties
        RoyaltyConfig[] memory royalties = _tokenRoyalty[tokenId];
        if (royalties.length == 0) {
            // Get extension specific royalties
            address extension = _tokensExtension[tokenId];
            if (extension != address(0)) {
                if (ERC165Checker.supportsInterface(extension, type(ICreatorExtensionRoyalties).interfaceId)) {
                    (receivers, bps) = ICreatorExtensionRoyalties(extension).getRoyalties(address(this), tokenId);
                    // Extension override exists, just return that
                    if (receivers.length > 0) return (receivers, bps);
                }
                royalties = _extensionRoyalty[extension];
            }
        }
        if (royalties.length == 0) {
            // Get the default royalty
            royalties = _extensionRoyalty[address(0)];
        }
        
        if (royalties.length > 0) {
            receivers = new address payable[](royalties.length);
            bps = new uint256[](royalties.length);
            for (uint i; i < royalties.length;) {
                receivers[i] = royalties[i].receiver;
                bps[i] = royalties[i].bps;
                unchecked { ++i; }
            }
        }
    }

    /**
     * Helper to get royalty receivers for a token
     */
    function _getRoyaltyReceivers(uint256 tokenId) view internal returns (address payable[] memory recievers) {
        (recievers, ) = _getRoyalties(tokenId);
    }

    /**
     * Helper to get royalty basis points for a token
     */
    function _getRoyaltyBPS(uint256 tokenId) view internal returns (uint256[] memory bps) {
        (, bps) = _getRoyalties(tokenId);
    }

    function _getRoyaltyInfo(uint256 tokenId, uint256 value) view internal returns (address receiver, uint256 amount){
        (address payable[] memory receivers, uint256[] memory bps) = _getRoyalties(tokenId);
        require(receivers.length <= 1, "More than 1 royalty receiver");
        
        if (receivers.length == 0) {
            return (address(this), 0);
        }
        return (receivers[0], bps[0]*value/10000);
    }

    /**
     * Set royalties for a token
     */
    function _setRoyalties(uint256 tokenId, address payable[] calldata receivers, uint256[] calldata basisPoints) internal {
       _checkRoyalties(receivers, basisPoints);
        delete _tokenRoyalty[tokenId];
        _setRoyalties(receivers, basisPoints, _tokenRoyalty[tokenId]);
        emit RoyaltiesUpdated(tokenId, receivers, basisPoints);
    }

    /**
     * Set royalties for all tokens of an extension
     */
    function _setRoyaltiesExtension(address extension, address payable[] calldata receivers, uint256[] calldata basisPoints) internal {
        _checkRoyalties(receivers, basisPoints);
        delete _extensionRoyalty[extension];
        _setRoyalties(receivers, basisPoints, _extensionRoyalty[extension]);
        if (extension == address(0)) {
            emit DefaultRoyaltiesUpdated(receivers, basisPoints);
        } else {
            emit ExtensionRoyaltiesUpdated(extension, receivers, basisPoints);
        }
    }

    /**
     * Helper function to check that royalties provided are valid
     */
    function _checkRoyalties(address payable[] calldata receivers, uint256[] calldata basisPoints) private pure {
        require(receivers.length == basisPoints.length, "Invalid input");
        uint256 totalBasisPoints;
        for (uint i; i < basisPoints.length;) {
            totalBasisPoints += basisPoints[i];
            unchecked { ++i; }
        }
        require(totalBasisPoints < 10000, "Invalid total royalties");
    }

    /**
     * Helper function to set royalties
     */
    function _setRoyalties(address payable[] calldata receivers, uint256[] calldata basisPoints, RoyaltyConfig[] storage royalties) private {
        for (uint i; i < basisPoints.length;) {
            royalties.push(
                RoyaltyConfig(
                    {
                        receiver: receivers[i],
                        bps: uint16(basisPoints[i])
                    }
                )
            );
            unchecked { ++i; }
        }
    }

    /**
     * @dev See {ICreatorCore-getApproveTransfer}.
     */
    function getApproveTransfer() external view override returns (address) {
        return _approveTransferBase;
    }
}

// File: @manifoldxyz/creator-core-solidity/contracts/core/ERC721CreatorCore.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "../extensions/ERC721/IERC721CreatorExtensionApproveTransfer.sol";
import "../extensions/ERC721/IERC721CreatorExtensionBurnable.sol";
import "../permissions/ERC721/IERC721CreatorMintPermissions.sol";
import "./IERC721CreatorCore.sol";
import "./CreatorCore.sol";

/**
 * @dev Core ERC721 creator implementation
 */
abstract contract ERC721CreatorCore is CreatorCore, IERC721CreatorCore {

    uint256 constant public VERSION = 2;

    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(CreatorCore, IERC165) returns (bool) {
        return interfaceId == type(IERC721CreatorCore).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {CreatorCore-_setApproveTransferExtension}
     */
    function _setApproveTransferExtension(address extension, bool enabled) internal override {
        if (ERC165Checker.supportsInterface(extension, type(IERC721CreatorExtensionApproveTransfer).interfaceId)) {
            _extensionApproveTransfers[extension] = enabled;
            emit ExtensionApproveTransferUpdated(extension, enabled);
        }
    }

    /**
     * @dev Set the base contract's approve transfer contract location
     */
    function _setApproveTransferBase(address extension) internal {
        _approveTransferBase = extension;
        emit ApproveTransferUpdated(extension);
    }

    /**
     * @dev Set mint permissions for an extension
     */
    function _setMintPermissions(address extension, address permissions) internal {
        require(_extensions.contains(extension), "CreatorCore: Invalid extension");
        require(permissions == address(0) || ERC165Checker.supportsInterface(permissions, type(IERC721CreatorMintPermissions).interfaceId), "Invalid address");
        if (_extensionPermissions[extension] != permissions) {
            _extensionPermissions[extension] = permissions;
            emit MintPermissionsUpdated(extension, permissions, msg.sender);
        }
    }

    /**
     * Check if an extension can mint
     */
    function _checkMintPermissions(address to, uint256 tokenId) internal {
        if (_extensionPermissions[msg.sender] != address(0)) {
            IERC721CreatorMintPermissions(_extensionPermissions[msg.sender]).approveMint(msg.sender, to, tokenId);
        }
    }

    /**
     * Override for post mint actions
     */
    function _postMintBase(address, uint256) internal virtual {}

    
    /**
     * Override for post mint actions
     */
    function _postMintExtension(address, uint256) internal virtual {}

    /**
     * Post-burning callback and metadata cleanup
     */
    function _postBurn(address owner, uint256 tokenId) internal virtual {
        // Callback to originating extension if needed
        if (_tokensExtension[tokenId] != address(0)) {
           if (ERC165Checker.supportsInterface(_tokensExtension[tokenId], type(IERC721CreatorExtensionBurnable).interfaceId)) {
               IERC721CreatorExtensionBurnable(_tokensExtension[tokenId]).onBurn(owner, tokenId);
           }
        }
        // Clear metadata (if any)
        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }    
        // Delete token origin extension tracking
        delete _tokensExtension[tokenId];    
    }

    /**
     * Approve a transfer
     */
    function _approveTransfer(address from, address to, uint256 tokenId) internal {
       if (_extensionApproveTransfers[_tokensExtension[tokenId]]) {
           require(IERC721CreatorExtensionApproveTransfer(_tokensExtension[tokenId]).approveTransfer(msg.sender, from, to, tokenId), "Extension approval failure");
       } else if (_approveTransferBase != address(0)) {
          require(IERC721CreatorExtensionApproveTransfer(_approveTransferBase).approveTransfer(msg.sender, from, to, tokenId), "Extension approval failure");
       }
    }

}
// File: @manifoldxyz/creator-core-solidity/contracts/core/ICreatorCore.sol



// 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 ApproveTransferUpdated(address extension);
    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);

    /**
     * @dev Set the default approve transfer contract location.
     */
    function setApproveTransfer(address extension) external; 

    /**
     * @dev Get the default approve transfer contract location.
     */
    function getApproveTransfer() external view returns (address);
}

// File: @manifoldxyz/creator-core-solidity/contracts/core/IERC721CreatorCore.sol



// 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;

}
// File: @manifoldxyz/creator-core-solidity/contracts/extensions/ERC721/IERC721CreatorExtensionApproveTransfer.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * Implement this if you want your extension to approve a transfer
 */
interface IERC721CreatorExtensionApproveTransfer is IERC165 {

    /**
     * @dev Set whether or not the creator will check the extension for approval of token transfer
     */
    function setApproveTransfer(address creator, bool enabled) external;

    /**
     * @dev Called by creator contract to approve a transfer
     */
    function approveTransfer(address operator, address from, address to, uint256 tokenId) external returns (bool);
}

// File: @manifoldxyz/creator-core-solidity/contracts/extensions/ERC721/IERC721CreatorExtensionBurnable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @dev Your extension is required to implement this interface if it wishes
 * to receive the onBurn callback whenever a token the extension created is
 * burned
 */
interface IERC721CreatorExtensionBurnable is IERC165 {
    /**
     * @dev callback handler for burn events
     */
    function onBurn(address owner, uint256 tokenId) external;
}
// File: @manifoldxyz/creator-core-solidity/contracts/extensions/ICreatorExtensionRoyalties.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @dev Implement this if you want your extension to have overloadable royalties
 */
interface ICreatorExtensionRoyalties is IERC165 {

    /**
     * Get the royalties for a given creator/tokenId
     */
    function getRoyalties(address creator, uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
}

// File: @manifoldxyz/creator-core-solidity/contracts/extensions/ICreatorExtensionTokenURI.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @dev Implement this if you want your extension to have overloadable URI's
 */
interface ICreatorExtensionTokenURI is IERC165 {

    /**
     * Get the uri for a given creator/tokenId
     */
    function tokenURI(address creator, uint256 tokenId) external view returns (string memory);
}

// File: @manifoldxyz/creator-core-solidity/contracts/permissions/ERC721/IERC721CreatorMintPermissions.sol



// 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;
}
// File: @manifoldxyz/libraries-solidity/contracts/access/AdminControl.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./IAdminControl.sol";

abstract contract AdminControl is Ownable, IAdminControl, ERC165 {
    using EnumerableSet for EnumerableSet.AddressSet;

    // Track registered admins
    EnumerableSet.AddressSet private _admins;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IAdminControl).interfaceId
            || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Only allows approved admins to call the specified function
     */
    modifier adminRequired() {
        require(owner() == msg.sender || _admins.contains(msg.sender), "AdminControl: Must be owner or admin");
        _;
    }   

    /**
     * @dev See {IAdminControl-getAdmins}.
     */
    function getAdmins() external view override returns (address[] memory admins) {
        admins = new address[](_admins.length());
        for (uint i = 0; i < _admins.length(); i++) {
            admins[i] = _admins.at(i);
        }
        return admins;
    }

    /**
     * @dev See {IAdminControl-approveAdmin}.
     */
    function approveAdmin(address admin) external override onlyOwner {
        if (!_admins.contains(admin)) {
            emit AdminApproved(admin, msg.sender);
            _admins.add(admin);
        }
    }

    /**
     * @dev See {IAdminControl-revokeAdmin}.
     */
    function revokeAdmin(address admin) external override onlyOwner {
        if (_admins.contains(admin)) {
            emit AdminRevoked(admin, msg.sender);
            _admins.remove(admin);
        }
    }

    /**
     * @dev See {IAdminControl-isAdmin}.
     */
    function isAdmin(address admin) public override view returns (bool) {
        return (owner() == admin || _admins.contains(admin));
    }

}
// File: @manifoldxyz/libraries-solidity/contracts/access/IAdminControl.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @dev Interface for admin control
 */
interface IAdminControl is IERC165 {

    event AdminApproved(address indexed account, address indexed sender);
    event AdminRevoked(address indexed account, address indexed sender);

    /**
     * @dev gets address of all admins
     */
    function getAdmins() external view returns (address[] memory);

    /**
     * @dev add an admin.  Can only be called by contract owner.
     */
    function approveAdmin(address admin) external;

    /**
     * @dev remove an admin.  Can only be called by contract owner.
     */
    function revokeAdmin(address admin) external;

    /**
     * @dev checks whether or not given address is an admin
     * Returns True if they are
     */
    function isAdmin(address admin) external view returns (bool);

}
// File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol



// 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);
    }
}

// File: @openzeppelin/contracts/security/ReentrancyGuard.sol



// 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;
    }
}

// File: @openzeppelin/contracts/token/ERC1155/IERC1155.sol



// 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;
}

// File: @openzeppelin/contracts/token/ERC721/ERC721.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @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.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @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, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



// 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);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol)

pragma solidity ^0.8.0;

import "../ERC721.sol";

/**
 * @dev ERC721 token with storage based token URI management.
 */
abstract contract ERC721URIStorage is ERC721 {
    using Strings for uint256;

    // Optional mapping for token URIs
    mapping(uint256 => string) private _tokenURIs;

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }

        return super.tokenURI(tokenId);
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }

    /**
     * @dev See {ERC721-_burn}. This override additionally checks to see if a
     * token-specific URI was set for the token, and if so, it deletes the token URI from
     * the storage mapping.
     */
    function _burn(uint256 tokenId) internal virtual override {
        super._burn(tokenId);

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }
    }
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// File: @openzeppelin/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// 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;
    }
}

// File: @openzeppelin/contracts/utils/Counters.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol



// 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);
    }
}

// File: @openzeppelin/contracts/utils/introspection/ERC165.sol



// 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;
    }
}

// File: @openzeppelin/contracts/utils/introspection/ERC165Checker.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface,
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            _supportsERC165Interface(account, type(IERC165).interfaceId) &&
            !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && _supportsERC165Interface(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)
        internal
        view
        returns (bool[] memory)
    {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in _interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!_supportsERC165Interface(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     * Interface identification is specified in ERC-165.
     */
    function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// 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);
}

// File: @openzeppelin/contracts/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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. It 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)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 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) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// File: contracts/FiguresController.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

import "./FiguresToken.sol";
import "./IFiguresToken.sol";
import "./marketplace/contracts/IMarketplaceCore.sol";
import "./ILazyDeliveryMetadata.sol";
import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract FiguresController is
    ILazyDelivery,
    ILazyDeliveryMetadata,
    VRFConsumerBaseV2,
    ConfirmedOwner
{
    struct InternalIds {
        uint256 requestId;
        uint256 seed;
        uint256 tokenId;
        uint40 listingId;
    }

    VRFCoordinatorV2Interface immutable COORDINATOR;
    LinkTokenInterface immutable LINKTOKEN;

    // Chainlink params
    uint64 private s_subscriptionId;
    bytes32 private s_keyHash;
    uint32 private s_gasLimit = 2500000;

    // Addresses
    address payable private _treasuryAddress;
    address private _tokenAddress;
    address private _marketplaceAddress;

    // Current params
    InternalIds private _currentIds;

    uint48 private _auctionDuration = 86400; // 1 day auction
    bool private _isActive = true;
    bool private _enableReferrer = false;

    event RandomWordsRecieved();
    event RandomWordsRequested();

    function supportsInterface(bytes4 interfaceId)
        external
        pure
        override
        returns (bool)
    {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(ILazyDelivery).interfaceId ||
            interfaceId == type(ILazyDeliveryMetadata).interfaceId;
    }

    constructor(
        address vrfCoordinator,
        address linkTokenContract,
        bytes32 keyHash,
        address payable treasuryAddress,
        address tokenAddress,
        address marketplaceAddress
    ) ConfirmedOwner(msg.sender) VRFConsumerBaseV2(vrfCoordinator) {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        LINKTOKEN = LinkTokenInterface(linkTokenContract);
        s_keyHash = keyHash;
        _treasuryAddress = treasuryAddress;
        _tokenAddress = tokenAddress;
        _marketplaceAddress = marketplaceAddress;
    }

    function subscriptionId() public view onlyOwner returns (uint256) {
        return s_subscriptionId;
    }

    function currentRequestId() public view onlyOwner returns (uint256) {
        return _currentIds.requestId;
    }

    function currentListingId() public view returns (uint40) {
        return _currentIds.listingId;
    }

    function setGasLimit(uint32 newGasLimit) public onlyOwner {
        s_gasLimit = newGasLimit;
    }

    function setKeyHash(bytes32 newKeyHash) public onlyOwner {
        s_keyHash = newKeyHash;
    }

    function setAuctionDuration(uint48 duration) public onlyOwner {
        _auctionDuration = duration;
    }

    function setReferrer(bool enable) public onlyOwner {
        _enableReferrer = enable;
    }

    function stopAuctions() public onlyOwner {
        _isActive = false;
    }

    function setTokenId(uint256 tokenId) public onlyOwner {
        _currentIds.tokenId = tokenId;
    }

    function setMarketplaceAddress(address marketplaceAddress)
        public
        onlyOwner
    {
        _marketplaceAddress = marketplaceAddress;
    }

    function setTokenAddress(address tokenAddress) public onlyOwner {
        _tokenAddress = tokenAddress;
    }

    function setTreasuryAddress(address payable treasuryAddress)
        public
        onlyOwner
    {
        _treasuryAddress = treasuryAddress;
    }

    function withdrawETH(uint256 amount) public onlyOwner {
        payable(msg.sender).transfer(amount);
    }

    // Create a new subscription when the contract is initially deployed.
    function createNewSubscription() external onlyOwner returns (uint256) {
        s_subscriptionId = COORDINATOR.createSubscription();
        // Add this contract as a consumer of its own subscription.
        COORDINATOR.addConsumer(s_subscriptionId, address(this));
        return s_subscriptionId;
    }

    // Assumes this contract owns link.
    // 1000000000000000000 = 1 LINK
    function topUpSubscription(uint256 amount) external onlyOwner {
        LINKTOKEN.transferAndCall(
            address(COORDINATOR),
            amount,
            abi.encode(s_subscriptionId)
        );
    }

    function addConsumer(address consumerAddress) external onlyOwner {
        // Add a consumer contract to the subscription.
        COORDINATOR.addConsumer(s_subscriptionId, consumerAddress);
    }

    function removeConsumer(address consumerAddress) external onlyOwner {
        // Remove a consumer contract from the subscription.
        COORDINATOR.removeConsumer(s_subscriptionId, consumerAddress);
    }

    function cancelSubscription(address receivingWallet) external onlyOwner {
        // Cancel the subscription and send the remaining LINK to a wallet address.
        COORDINATOR.cancelSubscription(s_subscriptionId, receivingWallet);
        s_subscriptionId = 0;
        _isActive = false;
    }

    // Transfer this contract's funds to an address.
    // 1000000000000000000 = 1 LINK
    function withdraw(uint256 amount, address to) external onlyOwner {
        LINKTOKEN.transfer(to, amount);
    }

    function initialize() external onlyOwner {
        _currentIds.requestId = COORDINATOR.requestRandomWords(
            s_keyHash,
            s_subscriptionId,
            3, // Confirmations
            s_gasLimit, // Gas Limit
            1 // Number of random words
        );
        _isActive = true;
        emit RandomWordsRequested();
    }

    // Gets called by VRFConsumerBaseV2
    function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)
        internal
        override
    {
        require(requestId == _currentIds.requestId, "Invalid requestId");

        // Clear _currentRequestId (prevent re-entrancy)
        _currentIds.requestId = 0;

        // Random number seed
        _currentIds.seed = randomWords[0];
        _currentIds.tokenId++;

        emit RandomWordsRecieved();

        uint256[] memory gen = new uint256[](13);
        gen[0] = _currentIds.seed;

        for (uint8 k = 1; k < 13; k++) {
            gen[k] = (gen[k - 1] << 10);
        }

        // Initialize seed
        IFiguresToken(_tokenAddress).initializeSeed(gen);

        MarketplaceLib.ListingReceiver[]
            memory receivers = new MarketplaceLib.ListingReceiver[](1);
        receivers[0] = MarketplaceLib.ListingReceiver(_treasuryAddress, 10000);

        _currentIds.listingId = IMarketplaceCore(_marketplaceAddress)
            .createListing(
                MarketplaceLib.ListingDetails(
                    0, // Initial amount
                    MarketplaceLib.ListingType.INDIVIDUAL_AUCTION,
                    1,
                    1,
                    600, // Bidding extension interval
                    10, // Min bid increase (in bps)
                    address(0),
                    address(0),
                    0, // Start auction on first bid
                    _auctionDuration
                ),
                MarketplaceLib.TokenDetails(
                    _currentIds.tokenId,
                    address(this),
                    TokenLib.Spec.ERC721,
                    true
                ),
                MarketplaceLib.DeliveryFees(0, 0),
                receivers,
                _enableReferrer,
                true,
                "0x0"
            );
    }

    function deliver(
        uint40 listingId,
        address to,
        uint256 assetId,
        uint24 payableCount,
        uint256,
        address,
        uint256
    ) external override returns (uint256) {
        require(
            msg.sender == _marketplaceAddress &&
                listingId == _currentIds.listingId &&
                assetId == _currentIds.tokenId &&
                payableCount == 1,
            "Unauthorized"
        );

        // Deliver asset
        IFiguresToken(_tokenAddress).mint(
            _currentIds.tokenId,
            _currentIds.seed,
            to
        );

        // stop new auctions if not active
        if (!_isActive) {
            _currentIds.requestId = 0;
            _currentIds.listingId = 0;
            return _currentIds.tokenId;
        }

        // Kick off new random number request
        _currentIds.requestId = COORDINATOR.requestRandomWords(
            s_keyHash,
            s_subscriptionId,
            3, // Confirmations
            s_gasLimit, // Gas Limit
            1 // Number of random words
        );

        emit RandomWordsRequested();
        return _currentIds.tokenId;
    }

    function assetURI(uint256 assetId)
        external
        view
        override
        returns (string memory)
    {
        if (assetId == _currentIds.tokenId) {
            return
                IFiguresToken(_tokenAddress).render(_currentIds.seed, assetId);
        }
        return IERC721Metadata(_tokenAddress).tokenURI(assetId);
    }
}

// File: contracts/FiguresDoubles.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

import "./digits/FiguresDoubleDigitsLib.sol";
import "./FiguresUtilLib.sol";

library FiguresDoubles {
    function chooseStringsDoubles(
        uint8 number,
        uint8 index1,
        uint8 index2
    ) public pure returns (bool[][2] memory b) {
        FiguresUtilLib.FigStrings memory strings = getFigStringsDoubles(number);
        return
            FiguresUtilLib._chooseStringsDouble(
                number,
                strings.s1,
                strings.s2,
                index1,
                index2
            );
    }

    function getFigStringsDoubles(uint8 number)
        private
        pure
        returns (FiguresUtilLib.FigStrings memory)
    {
        FiguresUtilLib.FigStrings memory figStrings;

        do {
            if (number == 0) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS0S1,
                    8
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS0S2,
                    8
                );
                break;
            }
            if (number == 1) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS1S1,
                    24
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS1S2,
                    24
                );
                break;
            }
            if (number == 2) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS2S1,
                    24
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS2S2,
                    24
                );
                break;
            }
            if (number == 3) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS3S1,
                    24
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS3S2,
                    24
                );
                break;
            }
            if (number == 4) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS4S1,
                    9
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS4S2,
                    16
                );
                break;
            }
            if (number == 5) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS5S1,
                    24
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS5S2,
                    24
                );
                break;
            }
            if (number == 6) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS6S1,
                    24
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS6S2,
                    33
                );
                break;
            }
            if (number == 7) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS7S1,
                    13
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS7S2,
                    4
                );
                break;
            }
            if (number == 8) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS8S1,
                    36
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS8S2,
                    36
                );
                break;
            }
            if (number == 9) {
                figStrings.s1 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS9S1,
                    36
                );
                figStrings.s2 = FiguresUtilLib._assignValuesDouble(
                    FiguresDoubleDigitsLib.FS9S2,
                    24
                );
                break;
            }
        } while (false);

        return figStrings;
    }
}

// File: contracts/FiguresRenderLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

library FiguresRenderLib {
    function _concat(string memory a, string memory b)
        private
        pure
        returns (string memory)
    {
        return string(abi.encodePacked(a, b));
    }

    function _generateRect(
        uint256 x,
        uint256 y,
        uint256 width,
        uint256 height,
        string memory color,
        string memory style
    ) public pure returns (string memory) {
        string memory s = "";
        s = _concat(s, "<rect x='");
        s = _concat(s, Strings.toString(x));
        s = _concat(s, "' y='");
        s = _concat(s, Strings.toString(y));
        s = _concat(s, "' width='");
        s = _concat(s, Strings.toString(width));
        s = _concat(s, "' height='");
        s = _concat(s, Strings.toString(height));
        s = _concat(s, "' style='fill:rgb(");
        s = _concat(s, color);
        s = _concat(s, "); ");
        s = _concat(s, style);
        s = _concat(s, "'/>");
        return s;
    }

    function _generatePixelSVG(
        uint256 x,
        uint256 y,
        uint256 factor,
        string memory color
    ) public pure returns (string memory) {
        string memory s = "";
        s = _concat(s, "<rect x='");
        s = _concat(s, Strings.toString(x));
        s = _concat(s, "' y='");
        s = _concat(s, Strings.toString(y));
        s = _concat(s, "' width='");
        s = _concat(s, Strings.toString(factor));
        s = _concat(s, "' height='");
        s = _concat(s, Strings.toString(factor));
        s = _concat(s, "' style='fill:rgb(");
        s = _concat(s, color);
        s = _concat(s, "); mix-blend-mode: multiply;'/>");
        return s;
    }
}

// File: contracts/FiguresSingles.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

import "./digits/Figure0DigitLib.sol";
import "./digits/Figure1DigitLib.sol";
import "./digits/Figure2DigitLib.sol";
import "./digits/Figure3DigitLib.sol";
import "./digits/Figure4DigitLib.sol";
import "./digits/Figure5DigitLib.sol";
import "./digits/Figure6DigitLib.sol";
import "./digits/Figure7DigitLib.sol";
import "./digits/Figure8DigitLib.sol";
import "./digits/Figure9DigitLib.sol";
import "./FiguresUtilLib.sol";

library FiguresSingles {
    function chooseStringsSingles(
        uint8 number,
        uint8 index1,
        uint8 index2
    ) public pure returns (bool[][2] memory b) {
        FiguresUtilLib.FigStrings memory strings = getFigStringsSingles(number);
        return
            FiguresUtilLib._chooseStringsSingle(
                number,
                strings.s1,
                strings.s2,
                index1,
                index2
            );
    }

    function getFigStringsSingles(uint8 number)
        private
        pure
        returns (FiguresUtilLib.FigStrings memory)
    {
        FiguresUtilLib.FigStrings memory figStrings;

        do {
            if (number == 0) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure0DigitLib.S1(),
                    144
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure0DigitLib.S2(),
                    144
                );
                break;
            }
            if (number == 1) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure1DigitLib.S1(),
                    28
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure1DigitLib.S2(),
                    28
                );
                break;
            }
            if (number == 2) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure2DigitLib.S1(),
                    54
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure2DigitLib.S2(),
                    54
                );
                break;
            }
            if (number == 3) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure3DigitLib.S1(),
                    54
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure3DigitLib.S2(),
                    54
                );
                break;
            }
            if (number == 4) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure4DigitLib.S1(),
                    108
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure4DigitLib.S2(),
                    30
                );
                break;
            }
            if (number == 5) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure5DigitLib.S1(),
                    54
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure5DigitLib.S2(),
                    54
                );
                break;
            }
            if (number == 6) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure6DigitLib.S1(),
                    54
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure6DigitLib.S2(),
                    216
                );
                break;
            }
            if (number == 7) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure7DigitLib.S1(),
                    18
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure7DigitLib.S2(),
                    6
                );
                break;
            }
            if (number == 8) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure8DigitLib.S1(),
                    216
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure8DigitLib.S2(),
                    216
                );
                break;
            }
            if (number == 9) {
                figStrings.s1 = FiguresUtilLib._assignValuesSingle(
                    Figure9DigitLib.S1(),
                    216
                );
                figStrings.s2 = FiguresUtilLib._assignValuesSingle(
                    Figure9DigitLib.S2(),
                    54
                );
                break;
            }
        } while (false);

        return figStrings;
    }
}

// File: contracts/FiguresToken.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

import "./IFiguresToken.sol";
import "./FiguresSingles.sol";
import "./FiguresDoubles.sol";
import "./FiguresRenderLib.sol";
import "@manifoldxyz/creator-core-solidity/contracts/ERC721Creator.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract FiguresToken is ERC721Creator, IFiguresToken {
    using Counters for Counters.Counter;

    address public CONTROLLER_ADDRESS;

    string private _baseSvg = "";
    uint256 private _scaleFactor = 20;
    string[3] private _backgroundColors = [
        "255,120,211",
        "255,227,0",
        "25,180,255"
    ];

    struct RandomVariables {
        uint8 number;
        uint8[4] bgColor1Index;
        uint8[4] bgColor2Index;
        uint8 figureIndex1_1;
        uint8 figureIndex1_2;
        uint8 figureIndex2_1;
        uint8 figureIndex2_2;
    }

    mapping(uint256 => uint256) private _seeds;
    mapping(uint256 => RandomVariables) private _seedRandom;

    constructor() ERC721Creator("Figures", "FIG") {}

    function setControllerAddress(
        address controllerAddress
    ) external onlyOwner {
        CONTROLLER_ADDRESS = controllerAddress;
    }

    function getControllerAddress() public view returns (address) {
        return CONTROLLER_ADDRESS;
    }

    function withdrawETH(uint256 amount) public onlyOwner {
        payable(msg.sender).transfer(amount);
    }

    /**
     * @dev See {IFiguresToken-initializeSeed}.
     */
    function initializeSeed(uint256[] memory seed) external override {
        require(msg.sender == CONTROLLER_ADDRESS, "Permission denied");
        // generate all the random numbers you need here!
        _seedRandom[seed[0]] = RandomVariables({
            number: uint8(_random(100, seed[0])),
            bgColor1Index: [
                uint8(_random(20, seed[1])),
                uint8(_random(20, seed[2])),
                uint8(_random(20, seed[3])),
                uint8(_random(20, seed[4]))
            ],
            bgColor2Index: [
                uint8(_random(20, seed[5])),
                uint8(_random(20, seed[6])),
                uint8(_random(20, seed[7])),
                uint8(_random(20, seed[8]))
            ],
            // 360 is the max number of options for number '9'
            figureIndex1_1: uint8(_random(360, seed[9])),
            figureIndex1_2: uint8(_random(360, seed[10])),
            figureIndex2_1: uint8(_random(360, seed[11])),
            figureIndex2_2: uint8(_random(360, seed[12]))
        });
    }

    /**
     * @dev See {IFiguresToken-mint}.
     */
    function mint(
        uint256 tokenId,
        uint256 seed,
        address recipient
    ) external override {
        require(msg.sender == CONTROLLER_ADDRESS, "Permission denied");
        // Store render data for this token
        _seeds[tokenId] = seed;

        // Mint token
        _mint(recipient, tokenId);
    }

    /**
     * @dev See {IERC721-tokenURI}.
     */
    function tokenURI(
        uint256 tokenId
    ) public view override returns (string memory) {
        _requireMinted(tokenId);
        return render(_seeds[tokenId], tokenId);
    }

    function tokenNumber(
        uint256 tokenId
    ) public view returns (uint8) {
        _requireMinted(tokenId);
        return _seedRandom[_seeds[tokenId]].number;
    }

    /**
     * @dev See {IFiguresToken-render}.
     */
    function render(
        uint256 seed,
        uint256 assetId
    ) public view override returns (string memory) {
        uint256 n;
        string memory s;
        string memory attributes;

        RandomVariables memory variables = _seedRandom[seed];

        n = variables.number;
        s = _concat(s, "data:image/svg+xml;utf8,");
        s = _concat(
            s,
            "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' id='figures' width='"
        );
        s = _concat(s, Strings.toString(10 * _scaleFactor));
        s = _concat(s, "' height='");
        s = _concat(s, Strings.toString(10 * _scaleFactor));
        s = _concat(s, "'>");
        s = _concat(
            s,
            _drawBackground(variables.bgColor1Index, variables.bgColor2Index)
        );

        if (n < 10) {
            bool[][2] memory figArrays = FiguresSingles.chooseStringsSingles(
                uint8(n),
                variables.figureIndex1_1,
                variables.figureIndex1_2
            );
            s = _concat(
                s,
                _drawFigure(
                    false,
                    figArrays
                )
            );

        } else {
            uint8 leftNumber = uint8(n / 10);
            uint8 rightNumber = uint8(n % 10);

            bool[][2] memory figArraysLeft = FiguresDoubles.chooseStringsDoubles(
                leftNumber,
                variables.figureIndex1_1,
                variables.figureIndex1_2
            );

            bool[][2] memory figArraysRight = FiguresDoubles.chooseStringsDoubles(
                rightNumber,
                variables.figureIndex2_1,
                variables.figureIndex2_2
            );

            s = _concat(
                s,
                _drawFigure(
                    false,
                    figArraysLeft
                )
            );
            s = _concat(
                s,
                _drawFigure(
                    true,
                    figArraysRight
                )
            );
        }
        s = _concat(s, "</svg>");

        attributes = _concat('{"name":"Figure #', Strings.toString(assetId));

        attributes = _concat(attributes, '", "description":"Figure representation of ');

        attributes = _concat(attributes, Strings.toString(n));

        attributes = _concat(attributes, '.", "created_by":"Figures", "external_url":"https://figures.art/token/');

        attributes = _concat(attributes, Strings.toString(assetId));

        attributes = _concat(attributes, '", "attributes": [{"trait_type": "Number", "value":"');

        attributes = _concat(attributes, Strings.toString(n));

        attributes = _concat(attributes, '"}], "image":"');

        return
            string(
                abi.encodePacked(
                    'data:application/json;utf8,',
                    attributes,
                    s,
                    '"}'
                )
            );
    }

    function _random(
        uint256 number,
        uint256 count
    ) internal view returns (uint256) {
        return
            uint256(
                keccak256(
                    abi.encodePacked(
                        block.timestamp,
                        block.difficulty,
                        msg.sender,
                        count
                    )
                )
            ) % number;
    }

    function _concat(string memory a, string memory b)
        private
        pure
        returns (string memory)
    {
        return string(abi.encodePacked(a, b));
    }

    function _drawBackground(
        uint8[4] memory bgColor1Index,
        uint8[4] memory bgColor2Index
    ) internal view returns (string memory) {
        // Draw first layer of bg by laying 4 randomly colored tiled
        string memory s = "";

        for (uint256 i = 0; i < 4; i++) {
            uint bgColor1IndexAvailable = bgColor1Index[i] % 3;
            uint bgColor2IndexAvailable = bgColor2Index[i] % 4;
            s = _concat(s, "<g>");
            s = _concat(
                s,
                (
                    FiguresRenderLib._generateRect(
                        (i % 2) * uint256(5) * _scaleFactor,
                        (i / 2) * uint256(5) * _scaleFactor,
                        uint256(5) * _scaleFactor,
                        uint256(5) * _scaleFactor,
                        _backgroundColors[bgColor1IndexAvailable],
                        ""
                    )
                )
            );
            if (bgColor2IndexAvailable == 3) {
                s = _concat(s, "</g>");
                continue;
            }

            s = _concat(
                s,
                (
                    FiguresRenderLib._generateRect(
                        (i % 2) * uint256(5) * _scaleFactor,
                        (i / 2) * uint256(5) * _scaleFactor,
                        uint256(5) * _scaleFactor,
                        uint256(5) * _scaleFactor,
                        _backgroundColors[bgColor2IndexAvailable],
                        "mix-blend-mode: multiply;"
                    )
                )
            );
            s = _concat(s, "</g>");
        }
        return s;
    }

    function _drawFigure(
        bool rightOffset,
        bool[][2] memory figArrays
    ) internal view returns (string memory) {
        uint256 rightOffsetX = 0;

        if (rightOffset) {
            rightOffsetX = uint256(5) * _scaleFactor;
        }

        string memory s = "";

        // k is top and bottom
        for (uint256 k = 0; k < 2; k++) {
            // i is each pixel
            for (uint256 i = 0; i < figArrays[k].length; ++i) {
                uint8 size = uint8(figArrays[k].length / 5);
                if (figArrays[k][i]) {
                    continue;
                }
                uint256 xOffset = _scaleFactor;
                uint256 yOffset = _scaleFactor;
                uint256 x = (i % size) * xOffset + rightOffsetX;
                uint256 y = 0;

                if (k == 1) {
                    y = (5 * yOffset);
                }
                y += (i / size) * yOffset;

                s = _concat(s, FiguresRenderLib._generatePixelSVG(x, y, _scaleFactor, "255,120,211"));
            }
        }

        return s;
    }
}

// File: contracts/FiguresUtilLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library FiguresUtilLib {
    struct FigStrings {
        string[] s1;
        string[] s2;
    }

    function substring(
        string memory str,
        uint256 startIndex,
        uint256 endIndex
    ) public pure returns (string memory) {
        bytes memory strBytes = bytes(str);
        bytes memory result = new bytes(endIndex - startIndex);
        for (uint256 i = startIndex; i < endIndex; i++) {
            result[i - startIndex] = strBytes[i];
        }
        return string(result);
    }

    function _assignValuesSingle(string memory input, uint16 size)
        internal
        pure
        returns (string[] memory)
    {
        return _assignValues(input, size, 50);
    }

    function _assignValuesDouble(string memory input, uint16 size)
        internal
        pure
        returns (string[] memory)
    {
        return _assignValues(input, size, 25);
    }

    function _assignValues(
        string memory input,
        uint16 size,
        uint8 length
    ) internal pure returns (string[] memory) {
        string[] memory output = new string[](size);
        for (uint256 i = 0; i < size; i++) {
            output[i] = substring(input, i * length, i * length + length);
        }
        return output;
    }

    function _chooseStringsSingle(
        uint8 number,
        string[] memory strings1,
        string[] memory strings2,
        uint8 index1,
        uint8 index2
    ) internal pure returns (bool[][2] memory b) {
        return _chooseStrings(number, strings1, strings2, index1, index2, 50);
    }

    function _chooseStringsDouble(
        uint8 number,
        string[] memory strings1,
        string[] memory strings2,
        uint8 index1,
        uint8 index2
    ) internal pure returns (bool[][2] memory b) {
        return _chooseStrings(number, strings1, strings2, index1, index2, 25);
    }

    function _chooseStrings(
        uint8 number,
        string[] memory strings1,
        string[] memory strings2,
        uint8 index1,
        uint8 index2,
        uint8 length
    ) private pure returns (bool[][2] memory b) {
        string[2] memory s;
        // some arrays are shorter than the random number generated
        uint256 availableIndex1 = index1 % strings1.length;
        uint256 availableIndex2 = index2 % strings2.length;
        s[0] = strings1[availableIndex1];
        s[1] = strings2[availableIndex2];

        // Special cases for 0, 1, 7
        if (number == 0 || number == 1 || number == 7) {
            if (length == 25) {
                while (
                    keccak256(bytes(substring(s[0], 20, 24))) !=
                    keccak256(bytes(substring(s[1], 0, 4)))
                ) {
                    uint256 is2 = ((availableIndex2 + availableIndex1++) %
                        strings2.length);
                    s[1] = strings2[is2];
                }
            }
            if (length == 50) {
                while (
                    keccak256(bytes(substring(s[0], 40, 49))) !=
                    keccak256(bytes(substring(s[1], 0, 9)))
                ) {
                    uint256 is2 = ((availableIndex2 + availableIndex1++) %
                        strings2.length);
                    s[1] = strings2[is2];
                }
            }
        }

        b[0] = _returnBoolArray(s[0]);
        b[1] = _returnBoolArray(s[1]);

        return b;
    }

    function checkString(string memory s1, string memory s2) private pure {}

    function _returnBoolArray(string memory s)
        internal
        pure
        returns (bool[] memory)
    {
        bytes memory b = bytes(s);
        bool[] memory a = new bool[](b.length);
        for (uint256 i = 0; i < b.length; i++) {
            uint8 z = (uint8(b[i]));
            if (z == 48) {
                a[i] = true;
            } else if (z == 49) {
                a[i] = false;
            }
        }
        return a;
    }
}

// File: contracts/IFiguresToken.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

interface IFiguresToken {
    /**
     * Initialize seed random variables used to render token
     * Can only be called by controller
     */
    function initializeSeed(uint256[] memory seed) external;

    /**
     * Mint a token with the given seed.
     * Can only be called by controller
     */
    function mint(
        uint256 tokenId,
        uint256 seed,
        address recipient
    ) external;

    /**
     * Render token based on seed
     */
    function render(uint256 seed, uint256 assetId)
        external
        view
        returns (string memory s);
}

// File: contracts/ILazyDeliveryMetadata.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * Metadata for lazy delivery tokens
 */
interface ILazyDeliveryMetadata is IERC165 {

    function assetURI(uint256 assetId) external view returns(string memory);

}
// File: contracts/digits/Figure0DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure0DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000011111111001111111100000000000000000000000000000000111111100011111110000000000000000000000000000000000111111100011111110000000000000000000000000000000011111100001111110000000000000000000000000000000000011111100001111110000000000000000000000000000000000011111100001111110000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000111111110011111111001111111100000000000000000000001111111000111111100011111110000000000000000000000001111111000111111100011111110000000000000000000000111111000011111100001111110000000000000000000000000111111000011111100001111110000000000000000000000000111111000011111100001111110000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000111000000011100000001110000000000000000000000000000111000000011100000001110000000000000000000000000000111000000011100000001110000000000000000000000000000111000000011100000001110000000000000000000000000000111000000011100000001110000000000000000000000000000111000000011100000001110000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000001111111100111111110011111111001111111100000000000011111110001111111000111111100011111110000000000000011111110001111111000111111100011111110000000000001111110000111111000011111100001111110000000000000001111110000111111000011111100001111110000000000000001111110000111111000011111100001111110000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000001110000000111000000011100000001110000000000000000001110000000111000000011100000001110000000000000000001110000000111000000011100000001110000000000000000001110000000111000000011100000001110000000000000000001110000000111000000011100000001110000000000000000001110000000111000000011100000001110000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010";
    }

    function S2() public pure returns (string memory) {
        return
            "010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000000000000010000000001000000000100000000010000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000000000001100000000110000000011000000001100000000000011100000001110000000111000000011100000000000000000011100000001110000000111000000011100000000000000000011100000001110000000111000000011100000000000000000011100000001110000000111000000011100000000000000000011100000001110000000111000000011100000000000000000011100000001110000000111000000011100000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000000000011110000001111000000111100000011110000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000000001111100000111110000011111000001111100000000000011111100001111110000111111000011111100000000000000011111100001111110000111111000011111100000000000000011111100001111110000111111000011111100000000000011111110001111111000111111100011111110000000000000011111110001111111000111111100011111110000000000001111111100111111110011111111001111111100000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000000000000010000000001000000000100000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000000000001100000000110000000011000000000000000000000011100000001110000000111000000000000000000000000000011100000001110000000111000000000000000000000000000011100000001110000000111000000000000000000000000000011100000001110000000111000000000000000000000000000011100000001110000000111000000000000000000000000000011100000001110000000111000000000000000000000011110000001111000000111100000000000000000000000000011110000001111000000111100000000000000000000000000011110000001111000000111100000000000000000000000000011110000001111000000111100000000000000000000000000011110000001111000000111100000000000000000000001111100000111110000011111000000000000000000000000001111100000111110000011111000000000000000000000000001111100000111110000011111000000000000000000000000001111100000111110000011111000000000000000000000011111100001111110000111111000000000000000000000000011111100001111110000111111000000000000000000000000011111100001111110000111111000000000000000000000011111110001111111000111111100000000000000000000000011111110001111111000111111100000000000000000000001111111100111111110011111111000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000010000000001000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000000000001100000000110000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000000000011100000001110000000000000000000000000000000011110000001111000000000000000000000000000000000000011110000001111000000000000000000000000000000000000011110000001111000000000000000000000000000000000000011110000001111000000000000000000000000000000000000011110000001111000000000000000000000000000000001111100000111110000000000000000000000000000000000001111100000111110000000000000000000000000000000000001111100000111110000000000000000000000000000000000001111100000111110000000000000000000000000000000011111100001111110000000000000000000000000000000000011111100001111110000000000000000000000000000000000011111100001111110000000000000000000000000000000011111110001111111000000000000000000000000000000000011111110001111111000000000000000000000000000000001111111100111111110000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000";
    }
}

// File: contracts/digits/Figure1DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure1DigitLib {
    function S1() public pure returns (string memory) {
        return
            "00000000011000000001100000000110000000011000000001000000000100000000011000000001100000000110000000010000000001000000000100000000011000000001100000000100000000010000000001000000000100000000011000000001000000000111000000011100000001110000000111000000010000000001000000000111000000011100000001110000000100000000010000000001000000000111000000011100000001000000000100000000010000000001000000000111000000010000000001111000000111100000011110000001111000000100000000010000000001111000000111100000011110000001000000000100000000010000000001111000000111100000010000000001000000000100000000010000000001111000000100000000111110000011111000001111100000111110000011000000001100000000111110000011111000001111100000110000000011000000001100000000111110000011111000001100000000110000000011000000001100000000111110000011000000011111100001111110000111111000011111100001110000000111000000011111100001111110000111111000011100000001110000000111000000011111100001111110000111000000011100000001110000000111000000011111100001110000000011110000001111000000111100000011110000001100000000110000000011110000001111000000111100000011000000001100000000110000000011110000001111000000110000000011000000001100000000110000000011110000001100000000111000000011100000001110000000111000000011000000001100000000111000000011100000001110000000110000000011000000001100000000111000000011100000001100000000110000000011000000001100000000111000000011";
    }

    function S2() public pure returns (string memory) {
        return
            "10000000011000000001100000000110000000010000000000100000000110000000011000000001000000000000000000001000000001100000000100000000000000000000000000000010000000010000000000000000000000000000000000000000110000000111000000011100000001110000000100000000001100000001110000000111000000010000000000000000000011000000011100000001000000000000000000000000000000110000000100000000000000000000000000000000000000001110000001111000000111100000011110000001000000000011100000011110000001111000000100000000000000000000111000000111100000010000000000000000000000000000001110000001000000000000000000000000000000000000000011100000111110000011111000001111100000110000000000111000001111100000111110000011000000000000000000001110000011111000001100000000000000000000000000000011100000110000000000000000000000000000000000000000111000011111100001111110000111111000011100000000001110000111111000011111100001110000000000000000000011100001111110000111000000000000000000000000000000111000011100000000000000000000000000000000000000001100000011110000001111000000111100000011000000000011000000111100000011110000001100000000000000000000110000001111000000110000000000000000000000000000001100000011000000000000000000000000000000000000000010000000111000000011100000001110000000110000000000100000001110000000111000000011000000000000000000001000000011100000001100000000000000000000000000000010000000110000000000000000000000000000000000000000";
    }
}

// File: contracts/digits/Figure2DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure2DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000011111111101111111110111111111000000000000000000000111111110011111111001111111100000000000000000000001111111000111111100011111110000000000000000000000011111100001111110000111111000000000000000000000000111110000011111000001111100000000000000000000000001111000000111100000011110000000000000000000000000011100000001110000000111000000000000000000000000000110000000011000000001100000000000000000000000000001000000000100000000010000000000000000000000000000011111111101111111110000000000000000000000000000000111111110011111111000000000000000000000000000000001111111000111111100000000000000000000000000000000011111100001111110000000000000000000000000000000000111110000011111000000000000000000000000000000000001111000000111100000000000000000000000000000000000011100000001110000000000000000000000000000000000000110000000011000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000001111111110111111111000000000000000000000000000000011111111001111111100000000000000000000000000000000111111100011111110000000000000000000000000000000001111110000111111000000000000000000000000000000000011111000001111100000000000000000000000000000000000111100000011110000000000000000000000000000000000001110000000111000000000000000000000000000000000000011000000001100000000000000000000000000000000000000100000000010000000000000000000000000000011111111100000000000000000000000000000000000000000111111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000001111111110000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000111111111000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000010000000000000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000000000000010000000001000000000100000000000000000000000000001100000000110000000011000000000000000000000000000111000000011100000001110000000000000000000000000011110000001111000000111100000000000000000000000001111100000111110000011111000000000000000000000000111111000011111100001111110000000000000000000000011111110001111111000111111100000000000000000000001111111100111111110011111111000000000000000000000111111111011111111101111111110000000000000000000000000000010000000001000000000000000000000000000000000000001100000000110000000000000000000000000000000000000111000000011100000000000000000000000000000000000011110000001111000000000000000000000000000000000001111100000111110000000000000000000000000000000000111111000011111100000000000000000000000000000000011111110001111111000000000000000000000000000000001111111100111111110000000000000000000000000000000111111111011111111100000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000110000000011000000000000000000000000000000000000011100000001110000000000000000000000000000000000001111000000111100000000000000000000000000000000000111110000011111000000000000000000000000000000000011111100001111110000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000011111111101111111110000000000000000000000000000010000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000111111111000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000011111111100000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000001111111110000000000";
    }
}

// File: contracts/digits/Figure3DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure3DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000011111111101111111110111111111000000000000000000000111111110011111111001111111100000000000000000000001111111000111111100011111110000000000000000000000011111100001111110000111111000000000000000000000000111110000011111000001111100000000000000000000000001111000000111100000011110000000000000000000000000011100000001110000000111000000000000000000000000000110000000011000000001100000000000000000000000000001000000000100000000010000000000000000000000000000011111111101111111110000000000000000000000000000000111111110011111111000000000000000000000000000000001111111000111111100000000000000000000000000000000011111100001111110000000000000000000000000000000000111110000011111000000000000000000000000000000000001111000000111100000000000000000000000000000000000011100000001110000000000000000000000000000000000000110000000011000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000001111111110111111111000000000000000000000000000000011111111001111111100000000000000000000000000000000111111100011111110000000000000000000000000000000001111110000111111000000000000000000000000000000000011111000001111100000000000000000000000000000000000111100000011110000000000000000000000000000000000001110000000111000000000000000000000000000000000000011000000001100000000000000000000000000000000000000100000000010000000000000000000000000000011111111100000000000000000000000000000000000000000111111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000001111111110000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000111111111000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000010000000000000000000";
    }

    function S2() public pure returns (string memory) {
        return S1();
    }
}

// File: contracts/digits/Figure4DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure4DigitLib {
    function S1() public pure returns (string memory) {
        return
            "011111110101111111010111111101011111110100000000000111111101011111110101111111010000000000000000000001111111010111111101000000000000000000000000000000011111101101111110110111111011011111101100000000000111111011011111101101111110110000000000000000000001111110110111111011000000000000000000000000000000011111011101111101110111110111011111011100000000000111110111011111011101111101110000000000000000000001111101110111110111000000000000000000000000000000011110111101111011110111101111011110111100000000000111101111011110111101111011110000000000000000000001111011110111101111000000000000000000000000000000001111110100111111010011111101001111110100000000000011111101001111110100111111010000000000000000000000111111010011111101000000000000000000000000000000001111101100111110110011111011001111101100000000000011111011001111101100111110110000000000000000000000111110110011111011000000000000000000000000000000001111011100111101110011110111001111011100000000000011110111001111011100111101110000000000000000000000111101110011110111000000000000000000000000000000001110111100111011110011101111001110111100000000000011101111001110111100111011110000000000000000000000111011110011101111000000000000000000000000000000000111110100011111010001111101000111110100000000000001111101000111110100011111010000000000000000000000011111010001111101000000000000000000000000000000000111101100011110110001111011000111101100000000000001111011000111101100011110110000000000000000000000011110110001111011000000000000000000000000000000000111011100011101110001110111000111011100000000000001110111000111011100011101110000000000000000000000011101110001110111000000000000000000000000000000000110111100011011110001101111000110111100000000000001101111000110111100011011110000000000000000000000011011110001101111000000000000000000000000000000000011110100001111010000111101000011110100000000000000111101000011110100001111010000000000000000000000001111010000111101000000000000000000000000000000000011101100001110110000111011000011101100000000000000111011000011101100001110110000000000000000000000001110110000111011000000000000000000000000000000000011011100001101110000110111000011011100000000000000110111000011011100001101110000000000000000000000001101110000110111000000000000000000000000000000000010111100001011110000101111000010111100000000000000101111000010111100001011110000000000000000000000001011110000101111000000000000000000000000000000011111100101111110010111111001011111100100000000000111111001011111100101111110010000000000000000000001111110010111111001000000000000000000000000000000011111001101111100110111110011011111001100000000000111110011011111001101111100110000000000000000000001111100110111110011000000000000000000000000000000011110011101111001110111100111011110011100000000000111100111011110011101111001110000000000000000000001111001110111100111000000000000000000000000000000011110001101111000110111100011011110001100000000000111100011011110001101111000110000000000000000000001111000110111100011000000000000000000000000000000011111000101111100010111110001011111000100000000000111110001011111000101111100010000000000000000000001111100010111110001000000000000000000000000000000001111100100111110010011111001001111100100000000000011111001001111100100111110010000000000000000000000111110010011111001000000000000000000000000000000001111001100111100110011110011001111001100000000000011110011001111001100111100110000000000000000000000111100110011110011000000000000000000000000000000001110011100111001110011100111001110011100000000000011100111001110011100111001110000000000000000000000111001110011100111000000000000000000000000000000001110001100111000110011100011001110001100000000000011100011001110001100111000110000000000000000000000111000110011100011000000000000000000000000000000001111000100111100010011110001001111000100000000000011110001001111000100111100010000000000000000000000111100010011110001000000000000000000000000000000000111100100011110010001111001000111100100000000000001111001000111100100011110010000000000000000000000011110010001111001000000000000000000000000000000000111001100011100110001110011000111001100000000000001110011000111001100011100110000000000000000000000011100110001110011000000000000000000000000000000000110011100011001110001100111000110011100000000000001100111000110011100011001110000000000000000000000011001110001100111000000000000000000000000000000000110001100011000110001100011000110001100000000000001100011000110001100011000110000000000000000000000011000110001100011000000000000000000000000000000000111000100011100010001110001000111000100000000000001110001000111000100011100010000000000000000000000011100010001110001000000000000000000000000000000000011100100001110010000111001000011100100000000000000111001000011100100001110010000000000000000000000001110010000111001000000000000000000000000000000000011001100001100110000110011000011001100000000000000110011000011001100001100110000000000000000000000001100110000110011000000000000000000000000000000000010011100001001110000100111000010011100000000000000100111000010011100001001110000000000000000000000001001110000100111000000000000000000000000000000000010001100001000110000100011000010001100000000000000100011000010001100001000110000000000000000000000001000110000100011000000000000000000000000000000000011000100001100010000110001000011000100000000000000110001000011000100001100010000000000000000000000001100010000110001000000000000000000000000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000011111111011111111101111111110111111111010000000000111111101111111110111111111011111111101100000000001111110111111111011111111101111111110111000000000011111011111111101111111110111111111011110000000000000000000011111111011111111101111111110100000000000000000000111111101111111110111111111011000000000000000000001111110111111111011111111101110000000000000000000011111011111111101111111110111100000000000000000000000000000011111111011111111101000000000000000000000000000000111111101111111110110000000000000000000000000000001111110111111111011100000000000000000000000000000011111011111111101111000000000011111110011111111001111111100111111110010000000000111111001111111100111111110011111111001100000000001111100111111110011111111001111111100111000000000000000000001111111001111111100111111110010000000000000000000011111100111111110011111111001100000000000000000000111110011111111001111111100111000000000000000000000000000000111111100111111110010000000000000000000000000000001111110011111111001100000000000000000000000000000011111001111111100111000000000011111100011111110001111111000111111100010000000000111110001111111000111111100011111110001100000000000000000000111111000111111100011111110001000000000000000000001111100011111110001111111000110000000000000000000000000000001111110001111111000100000000000000000000000000000011111000111111100011000000000011111000011111100001111110000111111000010000000000000000000011111000011111100001111110000100000000000000000000000000000011111000011111100001";
    }
}

// File: contracts/digits/Figure5DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure5DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000000000000010000000001000000000100000000000000000000000000001100000000110000000011000000000000000000000000000111000000011100000001110000000000000000000000000011110000001111000000111100000000000000000000000001111100000111110000011111000000000000000000000000111111000011111100001111110000000000000000000000011111110001111111000111111100000000000000000000001111111100111111110011111111000000000000000000000111111111011111111101111111110000000000000000000000000000010000000001000000000000000000000000000000000000001100000000110000000000000000000000000000000000000111000000011100000000000000000000000000000000000011110000001111000000000000000000000000000000000001111100000111110000000000000000000000000000000000111111000011111100000000000000000000000000000000011111110001111111000000000000000000000000000000001111111100111111110000000000000000000000000000000111111111011111111100000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000110000000011000000000000000000000000000000000000011100000001110000000000000000000000000000000000001111000000111100000000000000000000000000000000000111110000011111000000000000000000000000000000000011111100001111110000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000011111111101111111110000000000000000000000000000010000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000111111111000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000011111111100000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000001111111110000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000011111111101111111110111111111000000000000000000000111111110011111111001111111100000000000000000000001111111000111111100011111110000000000000000000000011111100001111110000111111000000000000000000000000111110000011111000001111100000000000000000000000001111000000111100000011110000000000000000000000000011100000001110000000111000000000000000000000000000110000000011000000001100000000000000000000000000001000000000100000000010000000000000000000000000000011111111101111111110000000000000000000000000000000111111110011111111000000000000000000000000000000001111111000111111100000000000000000000000000000000011111100001111110000000000000000000000000000000000111110000011111000000000000000000000000000000000001111000000111100000000000000000000000000000000000011100000001110000000000000000000000000000000000000110000000011000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000001111111110111111111000000000000000000000000000000011111111001111111100000000000000000000000000000000111111100011111110000000000000000000000000000000001111110000111111000000000000000000000000000000000011111000001111100000000000000000000000000000000000111100000011110000000000000000000000000000000000001110000000111000000000000000000000000000000000000011000000001100000000000000000000000000000000000000100000000010000000000000000000000000000011111111100000000000000000000000000000000000000000111111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000001111111110000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000111111111000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000010000000000000000000";
    }
}

// File: contracts/digits/Figure6DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure6DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000000000000010000000001000000000100000000000000000000000000001100000000110000000011000000000000000000000000000111000000011100000001110000000000000000000000000011110000001111000000111100000000000000000000000001111100000111110000011111000000000000000000000000111111000011111100001111110000000000000000000000011111110001111111000111111100000000000000000000001111111100111111110011111111000000000000000000000111111111011111111101111111110000000000000000000000000000010000000001000000000000000000000000000000000000001100000000110000000000000000000000000000000000000111000000011100000000000000000000000000000000000011110000001111000000000000000000000000000000000001111100000111110000000000000000000000000000000000111111000011111100000000000000000000000000000000011111110001111111000000000000000000000000000000001111111100111111110000000000000000000000000000000111111111011111111100000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000110000000011000000000000000000000000000000000000011100000001110000000000000000000000000000000000001111000000111100000000000000000000000000000000000111110000011111000000000000000000000000000000000011111100001111110000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000011111111101111111110000000000000000000000000000010000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000111111111000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000011111111100000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000001111111110000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000001111111000111111100011111110000000000000000000000001111111000111111100011111110000000000000000000000111111110011111111001111111100000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000001111111000111111100000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000111111100011111110000000000000000000000000000000000111111100011111110000000000000000000000000000000011111111001111111100000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000";
    }
}

// File: contracts/digits/Figure7DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure7DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000000000000001111111100111111110011111111000000000000000000000011111110001111111000111111100000000000000000000000111111000011111100001111110000000000000000000000001111100000111110000011111000000000000000000000000011110000001111000000111100000000000000000000000000111000000011100000001110000000000000000000000000000000000000111111110011111111000000000000000000000000000000001111111000111111100000000000000000000000000000000011111100001111110000000000000000000000000000000000111110000011111000000000000000000000000000000000001111000000111100000000000000000000000000000000000011100000001110000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000001110000000";
    }

    function S2() public pure returns (string memory) {
        return
            "111111110011111111001111111100111111110011111111001111111000111111100011111110001111111000111111100011111100001111110000111111000011111100001111110000111110000011111000001111100000111110000011111000001111000000111100000011110000001111000000111100000011100000001110000000111000000011100000001110000000";
    }
}

// File: contracts/digits/Figure8DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure8DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000001111111000111111100011111110000000000000000000000001111111000111111100011111110000000000000000000000111111110011111111001111111100000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000001111111000111111100000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000111111100011111110000000000000000000000000000000000111111100011111110000000000000000000000000000000011111111001111111100000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000001111111000111111100011111110000000000000000000000001111111000111111100011111110000000000000000000000111111110011111111001111111100000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000001111111000111111100000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000111111100011111110000000000000000000000000000000000111111100011111110000000000000000000000000000000011111111001111111100000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000";
    }
}

// File: contracts/digits/Figure9DigitLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library Figure9DigitLib {
    function S1() public pure returns (string memory) {
        return
            "000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000000000001000000000100000000010000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000000000000110000000011000000001100000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000000000001110000000111000000011100000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000000001111000000111100000011110000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000000000111110000011111000001111100000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000000001111110000111111000011111100000000000000000000001111111000111111100011111110000000000000000000000001111111000111111100011111110000000000000000000000111111110011111111001111111100000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000000000001000000000100000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000000000000110000000011000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000000000001110000000111000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000000001111000000111100000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000000000111110000011111000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000000001111110000111111000000000000000000000000000000001111111000111111100000000000000000000000000000000001111111000111111100000000000000000000000000000000111111110011111111000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000100000000010000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000000000011000000001100000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000000000111000000011100000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000000000111100000011110000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000000011111000001111100000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000000111111000011111100000000000000000000000000000000111111100011111110000000000000000000000000000000000111111100011111110000000000000000000000000000000011111111001111111100000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000001110000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000000111110000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000001111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000011110000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000001111111100000000000";
    }

    function S2() public pure returns (string memory) {
        return
            "000000000011111111101111111110111111111000000000000000000000111111110011111111001111111100000000000000000000001111111000111111100011111110000000000000000000000011111100001111110000111111000000000000000000000000111110000011111000001111100000000000000000000000001111000000111100000011110000000000000000000000000011100000001110000000111000000000000000000000000000110000000011000000001100000000000000000000000000001000000000100000000010000000000000000000000000000011111111101111111110000000000000000000000000000000111111110011111111000000000000000000000000000000001111111000111111100000000000000000000000000000000011111100001111110000000000000000000000000000000000111110000011111000000000000000000000000000000000001111000000111100000000000000000000000000000000000011100000001110000000000000000000000000000000000000110000000011000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000001111111110111111111000000000000000000000000000000011111111001111111100000000000000000000000000000000111111100011111110000000000000000000000000000000001111110000111111000000000000000000000000000000000011111000001111100000000000000000000000000000000000111100000011110000000000000000000000000000000000001110000000111000000000000000000000000000000000000011000000001100000000000000000000000000000000000000100000000010000000000000000000000000000011111111100000000000000000000000000000000000000000111111110000000000000000000000000000000000000000001111111000000000000000000000000000000000000000000011111100000000000000000000000000000000000000000000111110000000000000000000000000000000000000000000001111000000000000000000000000000000000000000000000011100000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000001111111110000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000111111100000000000000000000000000000000000000000001111110000000000000000000000000000000000000000000011111000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000001110000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000111111111000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000111111000000000000000000000000000000000000000000001111100000000000000000000000000000000000000000000011110000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000010000000000000000000";
    }
}

// File: contracts/digits/FiguresDoubleDigitsLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

library FiguresDoubleDigitsLib {
    string public constant FS0S1 =
        "00000011100111001110011100000000000011100111001110000000000000000011100111000000000000000000000011100000000100001000010000100000000000000100001000010000000000000000000100001000000000000000000000000100";
    string public constant FS0S2 =
        "01110011100111001110000000111001110011100000000000011100111000000000000000001110000000000000000000000010000100001000010000000001000010000100000000000000100001000000000000000000010000000000000000000000";

    string public constant FS1S1 =
        "000011000110001100011000100001000011000110001100010000100001000011000110001000010000100001000011000100001110011100111001110010000100001110011100111001000010000100001110011100100001000010000100001110010000111101111011110111101000010000111101111011110100001000010000111101111010000100001000010000111101000111101111011110111101100011000111101111011110110001100011000111101111011000110001100011000111101100111101111011110111101110011100111101111011110111001110011100111101111011100111001110011100111101110001110011100111001110011000110001110011100111001100011000110001110011100110001100011000110001110011";

    string public constant FS1S2 =
        "100011000110001100010000010001100011000100000000001000110001000000000000000100010000000000000000000011001110011100111001000001100111001110010000000000110011100100000000000000011001000000000000000000001110111101111011110100000111011110111101000000000011101111010000000000000001110100000000000000000000110111101111011110110000011011110111101100000000001101111011000000000000000110110000000000000000000010111101111011110111000001011110111101110000000000101111011100000000000000010111000000000000000000001001110011100111001100000100111001110011000000000010011100110000000000000001001100000000000000000000";

    string public constant FS2S1 =
        "000001111011110111100000000000111001110011100000000000011000110001100000000000001000010000100000000000000111101111000000000000000011100111000000000000000001100011000000000000000000100001000000000000000000000000111101111000000000000000011100111000000000000000001100011000000000000000000100001000000000000001111000000000000000000000111000000000000000000000011000000000000000000000001000000000000000000000000000001111000000000000000000000111000000000000000000000011000000000000000000000001000000000000000000000000000001111000000000000000000000111000000000000000000000011000000000000000000000001000000000";

    string public constant FS2S2 =
        "000000111101111011110000000000001110011100111000000000000011000110001100000000000000100001000010000000000011110111100000000000000000111001110000000000000000001100011000000000000000000010000100000000000000000000011110111100000000000000000111001110000000000000000001100011000000000000000000010000100000000000111100000000000000000000001110000000000000000000000011000000000000000000000000100000000000000000000000000111100000000000000000000001110000000000000000000000011000000000000000000000000100000000000000000000000000111100000000000000000000001110000000000000000000000011000000000000000000000000100000";

    string public constant FS3S1 = FS2S1;

    string public constant FS3S2 = FS2S1;

    string public constant FS4S1 =
        "011010110101101011010000001101011010110100000000000110101101000000000000000001010010100101001010000000101001010010100000000000010100101000000000000000010010100101001010010000001001010010100100000000000100101001000000000000000";

    string public constant FS4S2 =
        "0000011101111011110111101000001101111011110111101100000110011100111001110010000010001100011000110001000000000011101111011110100000000001101111011110110000000000110011100111001000000000010001100011000100000000000000011101111010000000000000001101111011000000000000000110011100100000000000000010001100010000000000000000000011101000000000000000000001101100000000000000000000110010000000000000000000010001";

    string public constant FS5S1 = FS2S2;

    string public constant FS5S2 = FS3S2;

    string public constant FS6S1 = FS2S2;

    string public constant FS6S2 =
        "000000111001110011100000000000000000111001110000000000001110011100000000000000000000000000011100000000000000000111000000000000000001110000000000000000000000100001000010000000000000001000010000100000000000000010000100001000000000000100001000000000000000000001000010000000000000000000010000100000000000000000000001000010000000000000000000010000100000000000000000000100001000000000000100000000000000000000000001000000000000000000000000010000000000000000000000000001000000000000000000000000010000000000000000000000000100000000000000000000000000010000000000000000000000000100000000000000000000000001000000000000110000000000000000000000000000110000000000000000000000000000110000000000000110001100000000000000000000000110001100000000000000110001100000000000000000000000110001100000000000011000110001100000000000000110001100011000000";

    string public constant FS7S1 =
        "0000011110111101111011110000000000011110111101111000000000000000011110111100000000000000000000011110000000000000000000000000000000111001110011100111000000000000111001110011100000000000000000111001110000000000000000000000111000000011000110001100011000000000000011000110001100000000000000000011000110000000000000000000000011000";

    string public constant FS7S2 =
        "1111011110111101111011110111001110011100111001110011000110001100011000110001000010000100001000010000";

    string public constant FS8S1 =
        "000000111001110011100000000000000000111001110000000000001110011100000000000000000110001100011000000000000001100011000110000000000000000000000111000000000000000001110000000000000000011100000000000000000000001000010000100000000000000010000100001000000000000000100001000010000000000001100000000000000000000000000001100000000000000000000000000001100000000000000110000000000000000000000000000110000000000000000000000000000110000000000000000010000100000000000000000000100001000000000000000000001000010000000000001000010000000000000000000010000100000000000000000000100001000000000000000001100011000000000000000000000001100011000000000000001100011000000000000000000000001100011000000000000100000000000000000000000001000000000000000000000000010000000000000000000000000001000000000000000000000000010000000000000000000000000100000000000000000000000000010000000000000000000000000100000000000000000000000001000000";

    string public constant FS8S2 = FS8S1;

    string public constant FS9S1 =
        "000000111001110011100000000000011100111000000000000000000000011100111000000000000111000000000000000000000000000111000000000000000000000000000111000000000000100001000010000000000000001000010000100000000000000010000100001000000000000000001000010000000000000000000010000100000000000000000000100001000000000000100001000000000000000000001000010000000000000000000010000100000000000000000000000000010000000000000000000000000100000000000000000000000001000000000000000001000000000000000000000000010000000000000000000000000100000000000000000100000000000000000000000001000000000000000000000000010000000000000000000000000000000011000000000000000000110000000000000000001100000000000000000000000000000000001100000000000000000011000000000000000000110000000000000000000000000001100011000000000000011000110000000000000000000000001100011000000000000011000110000000000000000011000110001100000000000000110001100011000000";

    string public constant FS9S2 = FS2S1;
}

// File: contracts/marketplace/contracts/IIdentityVerifier.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IIdentityVerifier is IERC165 {

    /**
     *  @dev Verify that the buyer can purchase/bid
     *
     *  @param listingId      The listingId associated with this verification
     *  @param identity       The identity to verify
     *  @param tokenAddress   The tokenAddress associated with this verification
     *  @param tokenId        The tokenId associated with this verification
     *  @param requestCount   The number of items being requested to purchase/bid
     *  @param requestAmount  The amount being requested
     *  @param requestERC20   The erc20 token address of the amount (0x0 if ETH)
     *  @param data           Additional data needed to verify
     *
     */
    function verify(uint40 listingId, address identity, address tokenAddress, uint256 tokenId, uint24 requestCount, uint256 requestAmount, address requestERC20, bytes calldata data) external returns (bool);

}
// File: contracts/marketplace/contracts/ILazyDelivery.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface ILazyDelivery is IERC165 {

    /**
     *  @dev Deliver an asset and deliver to the specified party
     *  When implementing this interface, please ensure you restrict access.
     *  If using LazyDeliver.sol, you can use authorizedDelivererRequired modifier to restrict access. 
     *  Delivery can be for an existing asset or newly minted assets.
     * 
     *  @param listingId      The listingId associated with this delivery.  Useful for permissioning.
     *  @param to             The address to deliver the asset to
     *  @param assetId        The assetId to deliver
     *  @param payableCount   The number of assets to deliver
     *  @param payableAmount  The amount seller will receive upon delivery of asset
     *  @param payableERC20   The erc20 token address of the amount (0x0 if ETH)
     *  @param index          (Optional): Index value for certain sales methods, such as ranked auctions
     *
     *  @return any Only used for Ranked Auctions and represents the refund amount you want to give.
     *              Value is unused for all other listing types
     *
     *  Suggestion: If determining a refund amount based on total sales data, do not enable this function
     *              until the sales data is finalized and recorded in contract
     *
     *  Exploit Prevention for dynamic/random assignment
     *  1. Ensure attributes are not assigned until AFTER underlying mint if using _safeMint.
     *     This is to ensure a receiver cannot check attribute values on receive and revert transaction.
     *     However, even if this is the case, the recipient can wrap its mint in a contract that checks 
     *     post mint completion and reverts if unsuccessful.
     *  2. Ensure that "to" is not a contract address. This prevents a contract from doing the lazy 
     *     mint, which could exploit random assignment by reverting if they do not receive the desired
     *     item post mint.
     */
    function deliver(uint40 listingId, address to, uint256 assetId, uint24 payableCount, uint256 payableAmount, address payableERC20, uint256 index) external returns(uint256);

}
// File: contracts/marketplace/contracts/IMarketplaceCore.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "./libs/MarketplaceLib.sol";

/**
 * Core Marketplace interface
 */
interface IMarketplaceCore {
    event MarketplaceEnabled(address requestor, bool value);
    event MarketplaceFees(address requestor, uint16 feeBPS, uint16 referrerBPS);
    event MarketplaceSellerRegistry(address requestor, address registry);
    event MarketplaceWithdraw(
        address requestor,
        address erc20,
        uint256 amount,
        address receiver
    );
    event MarketplaceWithdrawEscrow(
        address requestor,
        address erc20,
        uint256 amount
    );
    event MarketplaceRoyaltyEngineUpdate(address royaltyEngineV1);

    /**
     * @dev Listing structure
     *
     * @param id              - id of listing
     * @param seller          - the selling party
     * @param finalized       - Whether or not this listing has completed accepting bids/purchases
     * @param totalSold       - total number of items sold.  This IS NOT the number of sales.  Number of sales is totalSold/details.totalPerSale.
     * @param marketplaceBPS  - Marketplace fee BPS
     * @param referrerBPS     - Referrer BPS
     * @param details         - ListingDetails.  Contains listing configuration
     * @param token           - TokenDetails.  Contains the details of token being sold
     * @param receivers       - Array of ListingReceiver structs.  If provided, will distribute sales proceeds to receivers accordingly.
     * @param fees            - DeliveryFees.  Contains the delivery fee configuration for the listing
     * @param bid             - Active bid.  Only valid for INDIVIDUAL_AUCTION (1 bid)
     */
    struct Listing {
        uint256 id;
        address payable seller;
        bool finalized;
        uint24 totalSold;
        uint16 marketplaceBPS;
        uint16 referrerBPS;
        MarketplaceLib.ListingDetails details;
        MarketplaceLib.TokenDetails token;
        MarketplaceLib.ListingReceiver[] receivers;
        MarketplaceLib.DeliveryFees fees;
        MarketplaceLib.Bid bid;
        bool offersAccepted;
    }

    /**
     * @dev Offer structure
     *
     * @param offerer     - The address that made the offer
     * @param amount      - The offer amount
     * @param timestamp   - The time the offer was made
     * @param accepted    - Whether or not the offer was accepted
     */
    struct Offer {
        address offerer;
        uint256 amount;
        uint48 timestamp;
        bool accepted;
    }

    /**
     * @dev Set marketplace fee
     */
    function setFees(uint16 marketplaceFeeBPS, uint16 marketplaceReferrerBPS)
        external;

    /**
     * @dev Set marketplace enabled
     */
    function setEnabled(bool enabled) external;

    /**
     * @dev Set marketplace seller registry
     */
    function setSellerRegistry(address registry) external;

    /**
     * @dev See RoyaltyEngineV1 location. Can only be set once
     */
    function setRoyaltyEngineV1(address royaltyEngineV1) external;

    /**
     * @dev Withdraw from treasury
     */
    function withdraw(uint256 amount, address payable receiver) external;

    /**
     * @dev Withdraw from treasury
     */
    function withdraw(
        address erc20,
        uint256 amount,
        address payable receiver
    ) external;

    /**
     * @dev Withdraw from escrow
     */
    function withdrawEscrow(uint256 amount) external;

    /**
     * @dev Withdraw from escrow
     */
    function withdrawEscrow(address erc20, uint256 amount) external;

    /**
     * @dev Create listing
     */
    function createListing(
        MarketplaceLib.ListingDetails calldata listingDetails,
        MarketplaceLib.TokenDetails calldata tokenDetails,
        MarketplaceLib.DeliveryFees calldata deliveryFees,
        MarketplaceLib.ListingReceiver[] calldata listingReceivers,
        bool enableReferrer,
        bool acceptOffers,
        bytes calldata data
    ) external returns (uint40);

    /**
     * @dev Modify listing
     */
    function modifyListing(
        uint40 listingId,
        uint256 initialAmount,
        uint48 startTime,
        uint48 endTime
    ) external;

    /**
     * @dev Purchase a listed item
     */
    function purchase(uint40 listingId) external payable;

    function purchase(uint40 listingId, bytes calldata data) external payable;

    /**
     * @dev Purchase a listed item (with a referrer)
     */
    function purchase(address referrer, uint40 listingId) external payable;

    function purchase(
        address referrer,
        uint40 listingId,
        bytes calldata data
    ) external payable;

    /**
     * @dev Purchase a listed item
     */
    function purchase(uint40 listingId, uint24 count) external payable;

    function purchase(
        uint40 listingId,
        uint24 count,
        bytes calldata data
    ) external payable;

    /**
     * @dev Purchase a listed item (with a referrer)
     */
    function purchase(
        address referrer,
        uint40 listingId,
        uint24 count
    ) external payable;

    function purchase(
        address referrer,
        uint40 listingId,
        uint24 count,
        bytes calldata data
    ) external payable;

    /**
     * @dev Bid on a listed item
     */
    function bid(uint40 listingId, bool increase) external payable;

    function bid(
        uint40 listingId,
        bool increase,
        bytes calldata data
    ) external payable;

    /**
     * @dev Bid on a listed item (with a referrer)
     */
    function bid(
        address payable referrer,
        uint40 listingId,
        bool increase
    ) external payable;

    function bid(
        address payable referrer,
        uint40 listingId,
        bool increase,
        bytes calldata data
    ) external payable;

    /**
     * @dev Bid on a listed item
     */
    function bid(
        uint40 listingId,
        uint256 bidAmount,
        bool increase
    ) external;

    function bid(
        uint40 listingId,
        uint256 bidAmount,
        bool increase,
        bytes calldata data
    ) external;

    /**
     * @dev Bid on a listed item (with a referrer)
     */
    function bid(
        address payable referrer,
        uint40 listingId,
        uint256 bidAmount,
        bool increase
    ) external;

    function bid(
        address payable referrer,
        uint40 listingId,
        uint256 bidAmount,
        bool increase,
        bytes calldata data
    ) external;

    /**
     * @dev Make offer on a listed item
     */
    function offer(uint40 listingId, bool increase) external payable;

    function offer(
        uint40 listingId,
        bool increase,
        bytes calldata data
    ) external payable;

    /**
     * @dev Make offer on a listed item (with a referrer)
     */
    function offer(
        address payable referrer,
        uint40 listingId,
        bool increase
    ) external payable;

    function offer(
        address payable referrer,
        uint40 listingId,
        bool increase,
        bytes calldata data
    ) external payable;

    /**
     * @dev Make offer on a listed item
     */
    function offer(
        uint40 listingId,
        uint256 offerAmount,
        bool increase
    ) external;

    function offer(
        uint40 listingId,
        uint256 offerAmount,
        bool increase,
        bytes calldata data
    ) external;

    /**
     * @dev Make offer on a listed item (with a referrer)
     */
    function offer(
        address payable referrer,
        uint40 listingId,
        uint256 offerAmount,
        bool increase
    ) external;

    function offer(
        address payable referrer,
        uint40 listingId,
        uint256 offerAmount,
        bool increase,
        bytes calldata data
    ) external;

    /**
     * @dev Rescind an offer on a listed item
     */
    function rescind(uint40 listingId) external;

    function rescind(uint40[] calldata listingIds) external;

    function rescind(uint40 listingId, address[] calldata offerAddresses)
        external;

    /**
     * @dev Accept offer(s) on a listed item
     */
    function accept(
        uint40 listingId,
        address[] calldata addresses,
        uint256[] calldata amounts,
        uint256 maxAmount
    ) external;

    /**
     * @dev Collect proceeds of sale.  Only valid for non-lazy auctions where the asset
     * is in escrow
     */
    function collect(uint40 listingId) external;

    /**
     * @dev Finalize a listed item (post-purchase)
     */
    function finalize(uint40 listingId) external payable;

    /**
     * @dev Cancel listing
     */
    function cancel(uint40 listingId, uint16 holdbackBPS) external;

    /**
     * @dev Get listing details
     */
    function getListing(uint40 listingId)
        external
        view
        returns (Listing memory);

    /**
     * @dev Get the listing's current price
     */
    function getListingCurrentPrice(uint40 listingId)
        external
        view
        returns (uint256);

    /**
     * @dev Get the listing's deliver fee
     */
    function getListingDeliverFee(uint40 listingId, uint256 price)
        external
        view
        returns (uint256);

    /**
     * @dev Get the total listing price for multiple items
     */
    function getListingTotalPrice(uint40 listingId, uint24 count)
        external
        view
        returns (uint256);

    /**
     * @dev Returns bids of a listing. No ordering guarantees
     */
    function getBids(uint40 listingId)
        external
        view
        returns (MarketplaceLib.Bid[] memory);

    /**
     * @dev Returns offers of a listing. No ordering guarantees
     */
    function getOffers(uint40 listingId) external view returns (Offer[] memory);
}

// File: contracts/marketplace/contracts/IPriceEngine.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IPriceEngine is IERC165 {

    /**
     *  @dev Determine price of an asset given the number
     *  already minted.
     */
    function price(uint256 assetId, uint256 alreadyMinted, uint24 count) view external returns (uint256);

}
// File: contracts/marketplace/contracts/libs/MarketplaceLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

import "@manifoldxyz/libraries-solidity/contracts/access/IAdminControl.sol";

import "../IIdentityVerifier.sol";
import "../ILazyDelivery.sol";
import "../IPriceEngine.sol";

import "./TokenLib.sol";

/**
 * Interface for Ownable contracts
 */
interface IOwnable {
    function owner() external view returns (address);
}

/**
 * @dev Marketplace libraries
 */
library MarketplaceLib {
    using AddressUpgradeable for address;
    using EnumerableSet for EnumerableSet.AddressSet;

    // Events
    event CreateListing(
        uint40 indexed listingId,
        uint16 marketplaceBPS,
        uint16 referrerBPS,
        uint8 listingType,
        uint24 totalAvailable,
        uint24 totalPerSale,
        uint48 startTime,
        uint48 endTime,
        uint256 initialAmount,
        uint16 extensionInterval,
        uint16 minIncrementBPS,
        address erc20,
        address identityVerifier
    );
    event CreateListingTokenDetails(
        uint40 indexed listingId,
        uint256 id,
        address address_,
        uint8 spec,
        bool lazy
    );
    event CreateListingFees(
        uint40 indexed listingId,
        uint16 deliverBPS,
        uint240 deliverFixed
    );

    event PurchaseEvent(
        uint40 indexed listingId,
        address referrer,
        address buyer,
        uint24 count,
        uint256 amount
    );
    event BidEvent(
        uint40 indexed listingId,
        address referrer,
        address bidder,
        uint256 amount
    );
    event OfferEvent(
        uint40 indexed listingId,
        address referrer,
        address oferrer,
        uint256 amount
    );
    event RescindOfferEvent(
        uint40 indexed listingId,
        address oferrer,
        uint256 amount
    );
    event AcceptOfferEvent(
        uint40 indexed listingId,
        address oferrer,
        uint256 amount
    );
    event ModifyListing(
        uint40 indexed listingId,
        uint256 initialAmount,
        uint48 startTime,
        uint48 endTime
    );
    event CancelListing(
        uint40 indexed listingId,
        address requestor,
        uint16 holdbackBPS
    );
    event FinalizeListing(uint40 indexed listingId);

    // Listing types
    enum ListingType {
        INVALID,
        INDIVIDUAL_AUCTION,
        FIXED_PRICE,
        DYNAMIC_PRICE,
        OFFERS_ONLY
    }

    /**
     * @dev Listing structure
     *
     * @param seller          - the selling party
     * @param flags           - bit flag (hasBid, finalized, tokenCreator).  See FLAG_MASK_*
     * @param totalSold       - total number of items sold.  This IS NOT the number of sales.  Number of sales is totalSold/details.totalPerSale.
     * @param marketplaceBPS  - Marketplace fee BPS
     * @param referrerBPS     - Fee BPS for referrer if there is one
     * @param details         - ListingDetails.  Contains listing configuration
     * @param token           - TokenDetails.  Contains the details of token being sold
     * @param receivers       - Array of ListingReceiver structs.  If provided, will distribute sales proceeds to receivers accordingly.
     * @param bid             - Active bid.  Only valid for INDIVIDUAL_AUCTION (1 bid)
     * @param fees            - DeliveryFees.  Contains the delivery fee configuration for the listing
     */
    struct Listing {
        address payable seller;
        uint8 flags;
        uint24 totalSold;
        uint16 marketplaceBPS;
        uint16 referrerBPS;
        ListingDetails details;
        TokenDetails token;
        ListingReceiver[] receivers;
        Bid bid;
        DeliveryFees fees;
    }

    uint8 internal constant FLAG_MASK_HAS_BID = 0x1;
    uint8 internal constant FLAG_MASK_FINALIZED = 0x2;
    uint8 internal constant FLAG_MASK_TOKEN_CREATOR = 0x4;
    uint8 internal constant FLAG_MASK_ACCEPT_OFFERS = 0x8;

    /**
     * @dev Listing details structure
     *
     * @param initialAmount     - The initial amount of the listing. For auctions, it represents the reserve price.  For DYNAMIC_PRICE listings, it must be 0.
     * @param type_             - Listing type
     * @param totalAvailable    - Total number of tokens available.  Must be divisible by totalPerSale. For INDIVIDUAL_AUCTION, totalAvailable must equal totalPerSale
     * @param totalPerSale      - Number of tokens the buyer will get per purchase.  Must be 1 if it is a lazy token
     * @param extensionInterval - Only valid for *_AUCTION types. Indicates how long an auction will extend if a bid is made within the last <extensionInterval> seconds of the auction.
     * @param minIncrementBPS   - Only valid for *_AUCTION types. Indicates the minimum bid increase required
     * @param erc20             - If not 0x0, it indicates the erc20 token accepted for this sale
     * @param identityVerifier  - If not 0x0, it indicates the buyers should be verified before any bid or purchase
     * @param startTime         - The start time of the sale.  If set to 0, startTime will be set to the first bid/purchase.
     * @param endTime           - The end time of the sale.  If startTime is 0, represents the duration of the listing upon first bid/purchase.
     */
    struct ListingDetails {
        uint256 initialAmount;
        ListingType type_;
        uint24 totalAvailable;
        uint24 totalPerSale;
        uint16 extensionInterval;
        uint16 minIncrementBPS;
        address erc20;
        address identityVerifier;
        uint48 startTime;
        uint48 endTime;
    }

    /**
     * @dev Token detail structure
     *
     * @param address_  - The contract address of the token
     * @param id        - The token id (or for a lazy asset, the asset id)
     * @param spec      - The spec of the token.  If it's a lazy token, it must be blank.
     * @param lazy      - True if token is to be lazy minted, false otherwise.  If lazy, the contract address must support ILazyDelivery
     */
    struct TokenDetails {
        uint256 id;
        address address_;
        TokenLib.Spec spec;
        bool lazy;
    }

    /**
     * @dev Fee configuration for listing
     *
     * @param deliverBPS         - Additional fee needed to deliver the token (BPS)
     * @param deliverFixed       - Additional fee needed to deliver the token (fixed)
     */
    struct DeliveryFees {
        uint16 deliverBPS;
        uint240 deliverFixed;
    }

    /**
     * Listing receiver.  The array of listing receivers must add up to 10000 BPS if provided.
     */
    struct ListingReceiver {
        address payable receiver;
        uint16 receiverBPS;
    }

    /**
     * Represents an active bid
     *
     * @param amount       - The bid amount
     * @param bidder       - The bidder
     * @param delivered    - Whether or not the token has been delivered.
     * @param settled      - Whether or not the seller has been paid
     * @param refunded     - Whether or not the bid has been refunded
     * @param timestamp    - Timestamp of bid
     * @param referrer     - The referrer
     */
    struct Bid {
        uint256 amount;
        address payable bidder;
        bool delivered;
        bool settled;
        bool refunded;
        uint48 timestamp;
        address payable referrer;
    }

    /**
     * Represents an active offer
     *
     * @param amount        - The offer amount
     * @param timestamp     - Timestamp of offer
     * @param accepted      - Whether or not the offer was accepted (seller was paid)
     * @param referrer      - The referrer
     * @param erc20         - Currently unused.
     *                        Offers can only be made on the listing currency
     */
    struct Offer {
        uint200 amount;
        uint48 timestamp;
        bool accepted;
        address payable referrer;
        address erc20;
    }

    /**
     * Construct a marketplace listing
     */
    function constructListing(
        address seller,
        uint40 listingId,
        Listing storage listing,
        ListingDetails calldata listingDetails,
        TokenDetails calldata tokenDetails,
        DeliveryFees calldata deliveryFees,
        ListingReceiver[] calldata listingReceivers,
        bool acceptOffers,
        bool intake
    ) public {
        require(
            tokenDetails.address_.isContract(),
            "Token address must be a contract"
        );
        require(
            listingDetails.endTime > listingDetails.startTime,
            "End time must be after start time"
        );
        require(
            listingDetails.startTime == 0 ||
                listingDetails.startTime > block.timestamp,
            "Start and end time cannot occur in the past"
        );
        require(
            listingDetails.totalAvailable % listingDetails.totalPerSale == 0,
            "Invalid token config"
        );
        require(
            !acceptOffers ||
                listingDetails.type_ == ListingType.INDIVIDUAL_AUCTION,
            "Type cannot accept offers"
        );

        if (listingDetails.identityVerifier != address(0)) {
            require(
                ERC165Checker.supportsInterface(
                    listingDetails.identityVerifier,
                    type(IIdentityVerifier).interfaceId
                ),
                "Misconfigured verifier"
            );
        }

        if (listingReceivers.length > 0) {
            uint256 totalBPS;
            for (uint256 i; i < listingReceivers.length; ) {
                listing.receivers.push(listingReceivers[i]);
                totalBPS += listingReceivers[i].receiverBPS;
                unchecked {
                    ++i;
                }
            }
            require(totalBPS == 10000, "Invalid receiver config");
        }

        if (listingDetails.type_ == ListingType.INDIVIDUAL_AUCTION) {
            require(
                listingDetails.totalAvailable == listingDetails.totalPerSale,
                "Invalid token config"
            );
        } else if (listingDetails.type_ == ListingType.DYNAMIC_PRICE) {
            require(
                tokenDetails.lazy && listingDetails.initialAmount == 0,
                "Invalid listing config"
            );
            require(
                ERC165Checker.supportsInterface(
                    tokenDetails.address_,
                    type(IPriceEngine).interfaceId
                ),
                "Lazy delivered dynamic price items requires token address to implement IPriceEngine"
            );
        } else if (listingDetails.type_ == ListingType.OFFERS_ONLY) {
            require(
                listingDetails.initialAmount == 0 &&
                    listingDetails.startTime > block.timestamp,
                "Invalid listing config"
            );
        }

        // Purchase types
        if (isPurchase(listingDetails.type_) || isOffer(listingDetails.type_)) {
            require(
                deliveryFees.deliverBPS == 0 &&
                    deliveryFees.deliverFixed == 0 &&
                    listingDetails.extensionInterval == 0 &&
                    listingDetails.minIncrementBPS == 0,
                "Invalid listing config"
            );
        }

        if (tokenDetails.lazy) {
            require(listingDetails.totalPerSale == 1, "Invalid token config");
            require(
                ERC165Checker.supportsInterface(
                    tokenDetails.address_,
                    type(ILazyDelivery).interfaceId
                ),
                "Lazy delivery requires token address to implement ILazyDelivery"
            );
        } else {
            require(
                listingDetails.type_ == ListingType.INDIVIDUAL_AUCTION ||
                    listingDetails.type_ == ListingType.FIXED_PRICE,
                "Invalid type"
            );
            if (intake) {
                _intakeToken(
                    tokenDetails.spec,
                    tokenDetails.address_,
                    tokenDetails.id,
                    listingDetails.totalAvailable,
                    seller
                );
            }
        }

        // Set Listing Data
        listing.seller = payable(seller);
        listing.details = listingDetails;
        listing.token = tokenDetails;
        listing.fees = deliveryFees;

        // Token ownership check
        if (
            ERC165Checker.supportsInterface(
                tokenDetails.address_,
                type(IAdminControl).interfaceId
            ) && IAdminControl(tokenDetails.address_).isAdmin(seller)
        ) {
            listing.flags |= FLAG_MASK_TOKEN_CREATOR;
        } else {
            try IOwnable(tokenDetails.address_).owner() returns (
                address owner
            ) {
                if (owner == seller) listing.flags |= FLAG_MASK_TOKEN_CREATOR;
            } catch {}
        }

        if (acceptOffers) {
            listing.flags |= FLAG_MASK_ACCEPT_OFFERS;
        }

        _emitCreateListing(listingId, listing);
    }

    function _emitCreateListing(uint40 listingId, Listing storage listing)
        private
    {
        emit CreateListing(
            listingId,
            listing.marketplaceBPS,
            listing.referrerBPS,
            uint8(listing.details.type_),
            listing.details.totalAvailable,
            listing.details.totalPerSale,
            listing.details.startTime,
            listing.details.endTime,
            listing.details.initialAmount,
            listing.details.extensionInterval,
            listing.details.minIncrementBPS,
            listing.details.erc20,
            listing.details.identityVerifier
        );
        emit CreateListingTokenDetails(
            listingId,
            listing.token.id,
            listing.token.address_,
            uint8(listing.token.spec),
            listing.token.lazy
        );
        if (listing.fees.deliverBPS > 0 || listing.fees.deliverFixed > 0) {
            emit CreateListingFees(
                listingId,
                listing.fees.deliverBPS,
                listing.fees.deliverFixed
            );
        }
    }

    function _intakeToken(
        TokenLib.Spec tokenSpec,
        address tokenAddress,
        uint256 tokenId,
        uint256 tokensToTransfer,
        address from
    ) private {
        if (tokenSpec == TokenLib.Spec.ERC721) {
            require(
                tokensToTransfer == 1,
                "ERC721 invalid number of tokens to transfer"
            );
            TokenLib._erc721Transfer(
                tokenAddress,
                tokenId,
                from,
                address(this)
            );
        } else if (tokenSpec == TokenLib.Spec.ERC1155) {
            TokenLib._erc1155Transfer(
                tokenAddress,
                tokenId,
                tokensToTransfer,
                from,
                address(this)
            );
        } else {
            revert("Unsupported token spec");
        }
    }

    function isAuction(ListingType type_) internal pure returns (bool) {
        return (type_ == ListingType.INDIVIDUAL_AUCTION);
    }

    function isPurchase(ListingType type_) internal pure returns (bool) {
        return (type_ == ListingType.FIXED_PRICE ||
            type_ == ListingType.DYNAMIC_PRICE);
    }

    function isOffer(ListingType type_) internal pure returns (bool) {
        return (type_ == ListingType.OFFERS_ONLY);
    }

    function canOffer(ListingType type_, uint8 listingFlags)
        internal
        pure
        returns (bool)
    {
        // Can only make an offer if:
        // 1. Listing is an OFFERS_ONLY type
        // 2. Listing is an INDIVIDUAL_AUCTION that has offers enabled and no bids
        return (isOffer(type_) ||
            (isAuction(type_) &&
                (listingFlags & FLAG_MASK_ACCEPT_OFFERS) != 0 &&
                !hasBid(listingFlags)));
    }

    function hasBid(uint8 listingFlags) internal pure returns (bool) {
        return listingFlags & FLAG_MASK_HAS_BID != 0;
    }

    function isFinalized(uint8 listingFlags) internal pure returns (bool) {
        return listingFlags & FLAG_MASK_FINALIZED != 0;
    }

    function sellerIsTokenCreator(uint8 listingFlags)
        internal
        pure
        returns (bool)
    {
        return listingFlags & FLAG_MASK_TOKEN_CREATOR != 0;
    }

    function modifyListing(
        uint40 listingId,
        Listing storage listing,
        uint256 initialAmount,
        uint48 startTime,
        uint48 endTime
    ) public {
        require(listing.seller == msg.sender, "Permission denied");
        require(endTime > startTime, "End time must be after start time");
        require(
            startTime == 0 ||
                (startTime == listing.details.startTime &&
                    endTime > block.timestamp) ||
                startTime > block.timestamp,
            "Start and end time cannot occur in the past"
        );
        require(
            !isFinalized(listing.flags) &&
                ((!isAuction(listing.details.type_) &&
                    listing.totalSold == 0) ||
                    (isAuction(listing.details.type_) &&
                        listing.bid.amount == 0)),
            "Cannot modify listing that has already started or completed"
        );
        require(
            listing.details.type_ != ListingType.DYNAMIC_PRICE ||
                initialAmount == 0,
            "Invalid listing config"
        );
        listing.details.initialAmount = initialAmount;
        listing.details.startTime = startTime;
        listing.details.endTime = endTime;

        emit ModifyListing(listingId, initialAmount, startTime, endTime);
    }
}

// File: contracts/marketplace/contracts/libs/TokenLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

/**
 * @dev Token specs and functions
 */
library TokenLib {
    // Spec types
    enum Spec {
        NONE,
        ERC721,
        ERC1155
    }

    function _getSpecString(Spec spec) internal pure returns (string memory) {
        if (spec == Spec.ERC721) {
           return "erc721";
        } else if (spec == Spec.ERC1155) {
            return "erc1155";
        } else {
            return "";
        }
    }

    function _erc721Transfer(address tokenAddress, uint256 tokenId, address from, address to) internal {
        // Transfer token
        IERC721(tokenAddress).transferFrom(from, to, tokenId);
    }

    function _erc1155Transfer(address tokenAddress, uint256 tokenId, uint256 value, address from, address to) internal {
        // Transfer token
        IERC1155(tokenAddress).safeTransferFrom(from, to, tokenId, value, "");
    }

}