ETH Price: $2,047.11 (-1.92%)

Transaction Decoder

Block:
16351237 at Jan-07-2023 12:10:47 AM +UTC
Transaction Fee:
0.00180100429163694 ETH $3.69
Gas Used:
122,876 Gas / 14.657087565 Gwei

Emitted Events:

258 MintPassTwo.TransferSingle( operator=[Receiver] DOTS, from=[Sender] 0x2a7b8d58c7710ba1bfec4fa8e91aabef20d74228, to=0x00000000...000000000, id=0, value=1 )
259 DOTS.DOTMinted( tokenId=24480, evoNum=1, genes=563044947526919, mintPassTwoBurns=1 )
260 DOTS.Transfer( from=0x00000000...000000000, to=[Sender] 0x2a7b8d58c7710ba1bfec4fa8e91aabef20d74228, tokenId=24480 )

Account State Difference:

  Address   Before After State Difference Code
0x2a7b8d58...f20D74228
0.218624503898127937 Eth
Nonce: 538
0.216823499606490997 Eth
Nonce: 539
0.00180100429163694
0x33CfAe13...53822e8c7
(builder0x69)
2.034502292931522623 Eth2.034592485419279111 Eth0.000090192487756488
0xD07597b6...ed90C6Ce8

Execution Trace

DOTS.mint( _evoNum=1, _numDots=1 )
  • MintPassTwo.burnFromRedeem( _account=0x2a7b8d58C7710ba1BfEC4fa8E91aAbef20D74228, _amount=1 )
    File 1 of 2: DOTS
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
    import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
    import "erc721a/contracts/extensions/ERC721AQueryable.sol";
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "operator-filter-registry/src/OperatorFilterer.sol";
    /**
     * @title ERC721A token for DOTS
     * 
     * @dev DOTs redeemable through burning MintPassTwo tokens, earlier stage DOTS, or a combination of the two
     * 
     * @author Jack Chuma, NiftyDude
     */
    contract DOTS is ERC721AQueryable, AccessControl, VRFConsumerBaseV2, OperatorFilterer {
        
        bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
        uint8 constant NUM_BACKGROUND = 14;
        uint8 constant NUM_BACK = 12;
        uint8 constant NUM_PAINTJOB = 40;
        uint8 constant NUM_EYEWEAR = 31;
        uint8 constant NUM_MOUTHGEAR = 30;
        uint8 constant NUM_HEADGEAR = 40;
        uint8 constant NUM_CLOTHING = 40;
        uint8 constant NUM_EARGEAR = 20;
        uint16 constant BIT_MASK = 65535;
        IMintPassTwo immutable mintPassTwoContract;
        VRFCoordinatorV2Interface immutable COORDINATOR;
        string uri;
        bytes32 keyHash;
        uint64 subscriptionId;
        bool addApprovedContractsDisabled;
        uint32 callbackGasLimit = 2500000;
        uint16 requestConfirmations = 3;
        mapping(uint256 => uint256[]) public currentEvoDots; // evo # => tokenIds
        mapping(uint256 => Evo) public evoData; // evo # => evo info
        mapping(uint256 => Metadata) metadata; // tokenId => Metadata
        mapping(uint256 => bool) randomEntropies; // traitset => rolled
        mapping(uint256 => AnomalyRoll) public anomalyRolls;
        mapping(address => bool) public approvedContracts;
        struct AnomalyRoll {
            uint64 evoStage;
            uint64 anomalyAmount;
        }
        struct Evo {
            uint8 numTokensNeeded;
            uint32 startWindow;
            uint32 endWindow;
        }
        struct Metadata {
            uint8 evoNum;
            uint64 genes;
            uint8 anomalyNum;
        }
        error ValueTooHigh();
        error LengthMismatch();
        error MintWindowClosed();
        error NotOwnedBySender();
        error CannotBurnPrimaryToken();
        error MustIncludeAmount();
        error MustUpgradeEvoStage();
        error MustIncludeDotsToUpgrade();
        error BurnedEvoStageHigherThanTargetEvo();
        error TokenDoesNotExist();
        error InsufficientBaseAmount();
        error AddPreapprovedContractDisabled();
        error EvoDoesNotExist();
        event UriUpdated(string uri);
        event KeyHashSet(bytes32 keyhash);
        event CallbackGasLimitSet(uint256 limit);
        event RequestConfirmationsSet(uint256 confirmations);
        event SubscriptionIdSet(uint256 id);
        event ContractApprovalUpdated(address contractToUpdate, bool enabled);
        event AddingPreapprovedContractsDisabled(bool isDisabled);
        event DOTUpgraded(
            uint256 indexed tokenId, 
            uint256 indexed newEvoNum,
            uint256[] tokenIdsBurned,
            uint256 mintPassTwoBurns
        );
        event DotsUpgraded(
            uint256[] tokenIds,
            uint256 indexed newEvoNum,
            uint256 mintPassTwoBurns,
            uint256[] tokenIdsBurned
        );
        event DOTMinted(
            uint256 indexed tokenId, 
            uint256 indexed evoNum, 
            uint256 genes,
            uint256 mintPassTwoBurns
        );
        event EvoDataBatchUpdated(
            uint256[] evoNum, 
            uint256[] numTokensNeeded, 
            uint256[] startWindows, 
            uint256[] endWindows
        );
        event AnomalyRolled(
            uint256 indexed tokenId,
            uint256 indexed anomalyNum
        );
        constructor(
            string memory _name, 
            string memory _symbol,
            string memory _uri,
            address _mintPassTwo,
            Evo[] memory _evoData,
            address adminWallet,
            address _vrfCoordinator,
            bytes32 _keyHash,
            uint64 _subscriptionId,
            address _registrant
        ) 
            ERC721A(_name, _symbol) 
            VRFConsumerBaseV2(_vrfCoordinator) 
            OperatorFilterer(_registrant, true) 
        {
            COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator);
            subscriptionId = _subscriptionId;
            keyHash = _keyHash;
            uri = _uri;
            mintPassTwoContract = IMintPassTwo(_mintPassTwo);
            for (uint i=0; i<_evoData.length; ) {
                evoData[i + 1] = _evoData[i];
                unchecked { i++; }
            }
            _setupRole(DEFAULT_ADMIN_ROLE, adminWallet);
            _setupRole(ADMIN_ROLE, address(0xfd64b63D4A54e6b1a0Aa88e6623046c54F960D00));
        }
        function setKeyHash(bytes32 _keyHash) external onlyRole(ADMIN_ROLE) {
            keyHash = _keyHash;
            emit KeyHashSet(_keyHash);
        }
        function setCallbackGasLimit(uint32 _callbackGasLimit) external onlyRole(ADMIN_ROLE) {
            callbackGasLimit = _callbackGasLimit;
            emit CallbackGasLimitSet(_callbackGasLimit);
        }
        function setRequestConfirmations(uint16 _requestConfirmations) external onlyRole(ADMIN_ROLE) {
            requestConfirmations = _requestConfirmations;
            emit RequestConfirmationsSet(_requestConfirmations);
        }
        function setSubscriptionId(uint64 _subscriptionId) external onlyRole(ADMIN_ROLE) {
            subscriptionId = _subscriptionId;
            emit SubscriptionIdSet(_subscriptionId);
        }
        /**
         * @notice Called by contract admin to update stored data for a batch of EVO stages
         * @dev All input arrays must be arrays of same length
         * @param _evoNums Array of EVO stages to update data for
         * @param _numTokensNeeded Array representing new values for each edited EVO stage
         * @param _startWindows Array representing new startWindows for each edited EVO stage
         * @param _endWindows Array representing new endWindows for each edited EVO stage
         */
        function editEvoDataBatch(
            uint256[] calldata _evoNums,
            uint256[] calldata _numTokensNeeded,
            uint256[] calldata _startWindows,
            uint256[] calldata _endWindows
        ) external onlyRole(ADMIN_ROLE) {
            if (
                _evoNums.length != _numTokensNeeded.length || 
                _evoNums.length != _startWindows.length || 
                _evoNums.length != _endWindows.length
            ) revert LengthMismatch();
            for (uint i = 0; i < _evoNums.length; ) {
                evoData[_evoNums[i]] = Evo(uint8(_numTokensNeeded[i]), uint32(_startWindows[i]), uint32(_endWindows[i]));
                unchecked {  ++i; }
            }
            emit EvoDataBatchUpdated(_evoNums, _numTokensNeeded, _startWindows, _endWindows);
        }
        /**
         * @notice Called by contract admin to set a new base URI for DOTS
         */
        function setURI(string memory _uri) external onlyRole(ADMIN_ROLE) {
            uri = _uri;
            emit UriUpdated(_uri);
        }
        /**
         * @notice Called by contract admin to add / remove an approved contract
         * @param _approvedContract Contract address to add / remove
         * @param _enable Boolean value representing if contract should be enabled
         */
        function changeApprovedContract(
            address _approvedContract,
            bool _enable
        ) external onlyRole(ADMIN_ROLE) {
            if(addApprovedContractsDisabled && _enable) revert AddPreapprovedContractDisabled();
            approvedContracts[_approvedContract] = _enable;
            emit ContractApprovalUpdated(_approvedContract, _enable);
        } 
        /**
         * @notice Called by contact admin to disable adding new approved contracts
         */
        function irrevocablyDisableAddingPreapprovedContracts() external onlyRole(ADMIN_ROLE) {
            addApprovedContractsDisabled = true;
            emit AddingPreapprovedContractsDisabled(true);
        }
        /**
         * @notice admin function to initiate VRF transaction for anomaly distribution
         * @param _evoStage min evo stage for token to participate
         * @param _anomalyAmount amount of anomalies to distribute
         */
        function rollAnomalyDots(
            uint64 _evoStage,
            uint64 _anomalyAmount
        ) external onlyRole(ADMIN_ROLE) {
            if(currentEvoDots[_evoStage].length < _anomalyAmount) {
                revert InsufficientBaseAmount();
            }
            uint256 _requestId = COORDINATOR.requestRandomWords(
              keyHash,
              subscriptionId,
              requestConfirmations,
              callbackGasLimit,
              1
            );
            anomalyRolls[_requestId] = AnomalyRoll({
                evoStage: _evoStage,
                anomalyAmount: _anomalyAmount
            });
        }
        /**
         * @notice callback to retrieve random number for anomaly distribution
         * @param _requestId id of the request made by rollAnomalyDots
         * @param _randomWords the actual random number
         */
        function fulfillRandomWords(
            uint256 _requestId,
            uint256[] memory _randomWords
        ) internal override {
            AnomalyRoll memory anomalyRoll = anomalyRolls[_requestId];
            uint256[] memory _currentEvoIds = currentEvoDots[anomalyRoll.evoStage];
            uint256 _nonce;
            uint256 _numAssigned;
            uint256 _tempAnomalyToken;
            uint256 _hashForAnomalyNum;
            uint256 _anomalyNum;
            while (_numAssigned < anomalyRoll.anomalyAmount) {
                _tempAnomalyToken = _currentEvoIds[uint256(keccak256(abi.encodePacked(_randomWords[0], _nonce))) % _currentEvoIds.length];
                if(metadata[_tempAnomalyToken].anomalyNum == 0) {
                    _hashForAnomalyNum = uint256(keccak256(abi.encodePacked(_randomWords[0], _tempAnomalyToken))) % 100;
                    if (_hashForAnomalyNum < 10) _anomalyNum = 1;
                    else if (_hashForAnomalyNum < 55) _anomalyNum = 2;
                    else _anomalyNum = 3;
                    metadata[_tempAnomalyToken].anomalyNum = uint8(_anomalyNum);
                    unchecked { _numAssigned++; }
                    emit AnomalyRolled(_tempAnomalyToken, _anomalyNum);
                }
                unchecked { _nonce++; }               
            }
        } 
        /**
         * @notice Function to mint any EVO stage solely from burning correct number of MintPassTwo's
         * @dev Must be during proper mint window
         * @dev User must have enough MintPassTwo's in their wallet for burn
         * @dev Generates traits for dot
         * @param _evoNum EVO # to mint
         * @param _numDots Number of DOTs to mint
         */
        function mint(
            uint256 _evoNum, 
            uint256 _numDots
        ) external {
            _internalMint(_evoNum, _numDots, msg.sender);
        }
        
        /**
         * @notice Admin function to mint dots to a specified address
         */
        function mintTo(
            address _to,
            uint256 _evoNum, 
            uint256 _numDots
        ) external onlyRole(ADMIN_ROLE) {
            _internalMint(_evoNum, _numDots, _to);
        }
        /**
         * @notice For upgrading a DOT to a later EVO stage
         * @dev User must own at least one DOT to call this
         * @dev Any combination of DOTs and MintPassTwo's can be used to sum to value required for target EVO stage
         * @param _primaryTokenId Token ID of DOT to upgrade
         * @param _targetEvoNum EVO stage to upgrade to
         * @param _tokenIds Array of DOT token IDs to burn as part of the upgrade
         */
        function upgrade(
            uint256 _primaryTokenId, 
            uint256 _targetEvoNum, 
            uint256[] calldata _tokenIds
        ) external {
            Evo memory _info = evoData[_targetEvoNum];
            uint256 _oldEvoNum = metadata[_primaryTokenId].evoNum;
            _checkMintWindow(_info.startWindow, _info.endWindow);
            if (ownerOf(_primaryTokenId) != msg.sender) revert NotOwnedBySender();
            if (_targetEvoNum <= _oldEvoNum) revert MustUpgradeEvoStage();
            currentEvoDots[_targetEvoNum].push(_primaryTokenId);
            uint256 _diff;
            uint256 _valueFromDots;
            unchecked { 
                _valueFromDots = evoData[_oldEvoNum].numTokensNeeded 
                    + _burnDots(_primaryTokenId, _tokenIds); 
            }
            if (_valueFromDots > _info.numTokensNeeded) revert ValueTooHigh();
            unchecked { _diff = _info.numTokensNeeded - _valueFromDots; }
            if (_diff > 0) mintPassTwoContract.burnFromRedeem(msg.sender, _diff);
            metadata[_primaryTokenId].evoNum = uint8(_targetEvoNum);
            emit DOTUpgraded(_primaryTokenId, _targetEvoNum, _tokenIds, _diff);
        }
        /**
         * @notice For upgrading multiple DOTs in a single transaction
         * @param _primaryTokenIds Array of tokenIds of DOTs being upgraded
         * @param _targetEvoNum EVO stage that `_primaryTokenIds` are being upgraded to
         * @param _tokenIdsToBurn Array of tokenIds of DOTs being burned as part of upgrade
         */
        function upgradeMultiple(
            uint256[] calldata _primaryTokenIds,
            uint256 _targetEvoNum,
            uint256[] calldata _tokenIdsToBurn
        ) external {
            Evo memory _info = evoData[_targetEvoNum];
            _checkMintWindow(_info.startWindow, _info.endWindow);
            uint256 _diff;
            uint256 _valueFromDots;
            uint256 _totalValueNeeded;
            unchecked { 
                _totalValueNeeded = _info.numTokensNeeded * _primaryTokenIds.length;
                _valueFromDots = _validateAndUpgradePrimaryTokenIds(
                    _primaryTokenIds,
                    _targetEvoNum
                ) + _burnDotsUpgradeMultiple(_primaryTokenIds, _tokenIdsToBurn, _targetEvoNum);
            }
            if (_valueFromDots > _totalValueNeeded) revert ValueTooHigh();
            unchecked { _diff = _totalValueNeeded - _valueFromDots; }
            if (_diff > 0) mintPassTwoContract.burnFromRedeem(msg.sender, _diff);
            emit DotsUpgraded(
                _primaryTokenIds, 
                _targetEvoNum, 
                _diff,
                _tokenIdsToBurn 
            );
        }
        function burn(uint256 _tokenId) public {
            delete metadata[_tokenId];
            _burn(_tokenId, true);
        }
        function burnFromApprovedContract(
            uint256 _tokenId
        ) external {
            if(!approvedContracts[msg.sender]) revert TransferCallerNotOwnerNorApproved();
            delete metadata[_tokenId];
            _burn(_tokenId);
        }  
        function tokenURI(uint256 _id) public view override(ERC721A, IERC721A) returns (string memory) {
            return string(abi.encodePacked(uri, Strings.toString(_id)));
        }
        function getMetadata(uint256 tokenId) external view returns (Metadata memory _data) {
            _data = metadata[tokenId];
            if (_data.evoNum == 0) revert TokenDoesNotExist();
        }
        function supportsInterface(bytes4 interfaceId) public pure override(ERC721A, IERC721A, AccessControl) returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || 
                   interfaceId == type(IERC165).interfaceId ||
                   interfaceId == type(IERC721AQueryable).interfaceId ||
                   interfaceId == type(IERC721A).interfaceId ||
                   interfaceId == 0x80ac58cd ||
                   interfaceId == 0x5b5e139f;     
        }
        function setApprovalForAll(address operator, bool approved) public override(ERC721A, IERC721A) onlyAllowedOperatorApproval(operator) {
            super.setApprovalForAll(operator, approved);
        }
        function approve(address operator, uint256 tokenId) public override(ERC721A, IERC721A) onlyAllowedOperatorApproval(operator) {
            super.approve(operator, tokenId);
        }
        function transferFrom(address from, address to, uint256 tokenId) public override(ERC721A, IERC721A) onlyAllowedOperator(from) {
            super.transferFrom(from, to, tokenId);
        }
        function safeTransferFrom(address from, address to, uint256 tokenId) public override(ERC721A, IERC721A) onlyAllowedOperator(from) {
            super.safeTransferFrom(from, to, tokenId);
        }
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
            public
            override(ERC721A, IERC721A)
            onlyAllowedOperator(from)
        {
            super.safeTransferFrom(from, to, tokenId, data);
        }
        function _startTokenId() internal override view virtual returns (uint256) {
            return 1;
        }
        function _internalMint(
            uint256 _evoNum, 
            uint256 _numDots,
            address _to
        ) private {
            if (_numDots == 0) revert MustIncludeAmount();
            Evo memory _info = evoData[_evoNum];
            _checkMintWindow(_info.startWindow, _info.endWindow);
            mintPassTwoContract.burnFromRedeem(msg.sender, _info.numTokensNeeded * _numDots);
            uint256 _tokenId = _nextTokenId();
            for (uint i=0; i<_numDots; ) {
                if (_evoNum > 1) currentEvoDots[_evoNum].push(_tokenId);
                uint256 _genes = _generateTraits();
                metadata[_tokenId] = Metadata(uint8(_evoNum), uint64(_genes), uint8(0));
                randomEntropies[_genes >> 8] = true;
                emit DOTMinted(_tokenId, _evoNum, _genes, _info.numTokensNeeded);
                unchecked { 
                    i++; 
                    _tokenId++;
                }
            }
            _mint(_to, _numDots);
        }
        function _normalize(
            uint256 _rand
        ) private pure returns (uint256 _normalized) {
            uint256 _traitNumSelector = _rand & uint256(BIT_MASK);
            uint256[8] memory _traitIds = [
                _getId(_rand, uint256(16), uint256(65534), NUM_BACKGROUND),
                _getId(_rand, uint256(32), uint256(65532), NUM_BACK),
                _getId(_rand, uint256(48), uint256(65520), NUM_PAINTJOB),
                _getId(_rand, uint256(64), uint256(65534), NUM_EYEWEAR),
                _getId(_rand, uint256(80), uint256(65520), NUM_MOUTHGEAR),
                _getId(_rand, uint256(96), uint256(65520), NUM_HEADGEAR),
                _getId(_rand, uint256(112), uint256(65520), NUM_CLOTHING),
                _getId(_rand, uint256(128), uint256(65520), NUM_EARGEAR)
            ];
            if (_traitIds[5] == 35) _traitIds[7] = 0;
            _normalized =
                (_traitIds[0] << 0 |
                _traitIds[1] << 8 |
                _traitIds[2] << 16 |
                _traitIds[3] << 24 |
                _traitIds[4] << 32 |
                _traitIds[5] << 40 |
                _traitIds[6] << 48 |
                _traitIds[7] << 56)
                & _generateMask(_traitNumSelector);
        }
        function _getId(
            uint256 _rand, 
            uint256 _offset,
            uint256 _cutoff,
            uint256 _options
        ) private pure returns (uint256 _id) {
            uint256 _slice = (_rand & (uint256(BIT_MASK) << _offset)) >> _offset;
            while (_slice >= _cutoff) {
                _slice = uint256(uint16(uint256(keccak256(abi.encodePacked(_slice)))));
            }
            unchecked { _id = _slice % _options + 1; }
        }
        function _generateMask(uint256 _selector) private pure returns (uint256 _mask) {
            uint256 _eightBitMask = uint256(255);
            _mask = uint256(4294967295);
            if (_selector > 1637) {
                if (_selector < 24575) {
                    if (_selector < 9322) _mask = _mask | _eightBitMask << 48;
                    else if (_selector < 16891) _mask = _mask | _eightBitMask << 40;
                    else if (_selector < 24460) _mask = _mask | _eightBitMask << 32;
                    else _mask = _mask | _eightBitMask << 56;
                } else if (_selector < 57343) {
                    if (_selector < 37551) _mask = _mask | _eightBitMask << 40 | _eightBitMask << 48;
                    else if (_selector < 53771) _mask = _mask | _eightBitMask << 32 | _eightBitMask << 48;
                    else if (_selector < 54066)  _mask = _mask | _eightBitMask << 48 | _eightBitMask << 56;
                    else if (_selector < 57277) _mask = _mask | _eightBitMask << 32 | _eightBitMask << 40;
                    else if (_selector < 57306) _mask = _mask | _eightBitMask << 40 | _eightBitMask << 56;
                    else _mask = _mask | _eightBitMask << 32 | _eightBitMask << 56;
                } else if (_selector < 63897) {
                    if (_selector < 62974) _mask = _mask | _eightBitMask << 32 | _eightBitMask << 40 | _eightBitMask << 48;
                    else if (_selector < 63257) _mask = _mask | _eightBitMask << 40 | _eightBitMask << 48 | _eightBitMask << 56;
                    else if (_selector < 63568) _mask = _mask | _eightBitMask << 32 | _eightBitMask << 48 | _eightBitMask << 56;
                    else _mask = _mask | _eightBitMask << 32 | _eightBitMask << 40 | _eightBitMask << 56;
                } else _mask = _mask | _mask << 32;
            }
        }
        function _burnDots(
            uint256 _primaryTokenId, 
            uint256[] calldata _tokenIdsToBurn
        ) private returns (uint256 _value) {
            for (uint i=0; i<_tokenIdsToBurn.length; ) {
                uint256 _tokenId = _tokenIdsToBurn[i];
                if (_tokenId == _primaryTokenId) revert CannotBurnPrimaryToken();
                unchecked { 
                    _value += evoData[metadata[_tokenId].evoNum].numTokensNeeded;
                    i++;
                }
                burn(_tokenId);
            }
        }
        function _burnDotsUpgradeMultiple(
            uint256[] calldata _primaryTokenIds, 
            uint256[] calldata _tokenIdsToBurn,
            uint256 _targetEvoNum
        ) private returns (uint256 _value) {
            if (_primaryTokenIds.length == 0) revert MustIncludeDotsToUpgrade();
            for (uint i=0; i<_tokenIdsToBurn.length; ) {
                uint256 _tokenId = _tokenIdsToBurn[i];
                uint256 _evoNum = metadata[_tokenId].evoNum;
                if (_valueInArray(_tokenId, _primaryTokenIds)) revert CannotBurnPrimaryToken();
                if (_evoNum >= _targetEvoNum) revert BurnedEvoStageHigherThanTargetEvo();
                burn(_tokenId);
                unchecked { 
                    _value += evoData[_evoNum].numTokensNeeded;
                    i++; 
                }
            }
        }
        function _valueInArray(
            uint256 _value, 
            uint256[] calldata _arr
        ) private pure returns (bool) {
            for (uint i=0; i<_arr.length; ) {
                if (_arr[i] == _value) return true;
                unchecked { i++; }
            }
            return false;
        }
        function _validateAndUpgradePrimaryTokenIds(
            uint256[] calldata _primaryTokenIds, 
            uint256 _targetEvoNum
        ) private returns (uint256 _value) {
            for (uint i=0; i<_primaryTokenIds.length; ) {
                uint256 _primaryTokenId = _primaryTokenIds[i];
                uint256 _oldEvoNum = metadata[_primaryTokenId].evoNum;
                if (ownerOf(_primaryTokenId) != msg.sender) revert NotOwnedBySender();
                if (_targetEvoNum <= _oldEvoNum) revert MustUpgradeEvoStage();
                currentEvoDots[_targetEvoNum].push(_primaryTokenId);
                metadata[_primaryTokenId].evoNum = uint8(_targetEvoNum);
                unchecked { 
                    _value += evoData[_oldEvoNum].numTokensNeeded;
                    i++; 
                }
            }
        }
        function _generateTraits() private view returns (uint256 _genes) {
            uint256 _rand;
            unchecked { 
                _rand = uint256(
                    keccak256(
                        abi.encode(
                            keccak256(
                                abi.encodePacked(
                                    msg.sender, tx.origin, gasleft(), block.timestamp, block.number, blockhash(block.number), blockhash(block.number-100)
                                )
                            )
                        )
                    )
                );
            }
            while (true) {
                _genes = _normalize(_rand);
                if (!randomEntropies[_genes >> 8]) break;
                _rand = uint256(keccak256(abi.encodePacked(_rand)));
            }
        }
        function _checkMintWindow(uint256 _start, uint256 _end) private view {
            if (_end == 0) revert EvoDoesNotExist();
            if ((block.timestamp < _start || block.timestamp > _end) && !hasRole(ADMIN_ROLE, msg.sender)) revert MintWindowClosed();
        }
    }
    interface IMintPassTwo {
        function burnFromRedeem(address _account, uint256 _amount) external;
    }
    // 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);
      }
    }
    // 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);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721AQueryable.sol';
    import '../ERC721A.sol';
    /**
     * @title ERC721AQueryable.
     *
     * @dev ERC721A subclass with convenience query functions.
     */
    abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable {
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) {
            TokenOwnership memory ownership;
            if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
                return ownership;
            }
            ownership = _ownershipAt(tokenId);
            if (ownership.burned) {
                return ownership;
            }
            return _ownershipOf(tokenId);
        }
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] calldata tokenIds)
            external
            view
            virtual
            override
            returns (TokenOwnership[] memory)
        {
            unchecked {
                uint256 tokenIdsLength = tokenIds.length;
                TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                for (uint256 i; i != tokenIdsLength; ++i) {
                    ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                }
                return ownerships;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view virtual override returns (uint256[] memory) {
            unchecked {
                if (start >= stop) revert InvalidQueryRange();
                uint256 tokenIdsIdx;
                uint256 stopLimit = _nextTokenId();
                // Set `start = max(start, _startTokenId())`.
                if (start < _startTokenId()) {
                    start = _startTokenId();
                }
                // Set `stop = min(stop, stopLimit)`.
                if (stop > stopLimit) {
                    stop = stopLimit;
                }
                uint256 tokenIdsMaxLength = balanceOf(owner);
                // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                // to cater for cases where `balanceOf(owner)` is too big.
                if (start < stop) {
                    uint256 rangeLength = stop - start;
                    if (rangeLength < tokenIdsMaxLength) {
                        tokenIdsMaxLength = rangeLength;
                    }
                } else {
                    tokenIdsMaxLength = 0;
                }
                uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                if (tokenIdsMaxLength == 0) {
                    return tokenIds;
                }
                // We need to call `explicitOwnershipOf(start)`,
                // because the slot at `start` may not be initialized.
                TokenOwnership memory ownership = explicitOwnershipOf(start);
                address currOwnershipAddr;
                // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
                // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
                if (!ownership.burned) {
                    currOwnershipAddr = ownership.addr;
                }
                for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                // Downsize the array to fit.
                assembly {
                    mstore(tokenIds, tokenIdsIdx)
                }
                return tokenIds;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
            unchecked {
                uint256 tokenIdsIdx;
                address currOwnershipAddr;
                uint256 tokenIdsLength = balanceOf(owner);
                uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                TokenOwnership memory ownership;
                for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                return tokenIds;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `_msgSender()` is missing `role`.
         * Overriding this function changes the behavior of the {onlyRole} modifier.
         *
         * Format of the revert message is described in {_checkRole}.
         *
         * _Available since v4.6._
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(uint160(account), 20),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * May emit a {RoleGranted} event.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
    /**
     * @title  OperatorFilterer
     * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
     *         registrant's entries in the OperatorFilterRegistry.
     * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
     *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
     *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
     */
    abstract contract OperatorFilterer {
        error OperatorNotAllowed(address operator);
        IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
            IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
        constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
            // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
            // will not revert, but the contract will need to be registered with the registry once it is deployed in
            // order for the modifier to filter addresses.
            if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                if (subscribe) {
                    OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                } else {
                    if (subscriptionOrRegistrantToCopy != address(0)) {
                        OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                    } else {
                        OPERATOR_FILTER_REGISTRY.register(address(this));
                    }
                }
            }
        }
        modifier onlyAllowedOperator(address from) virtual {
            // Allow spending tokens from addresses with balance
            // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
            // from an EOA.
            if (from != msg.sender) {
                _checkFilterOperator(msg.sender);
            }
            _;
        }
        modifier onlyAllowedOperatorApproval(address operator) virtual {
            _checkFilterOperator(operator);
            _;
        }
        function _checkFilterOperator(address operator) internal view virtual {
            // Check registry code length to facilitate testing in environments without a deployed registry.
            if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                    revert OperatorNotAllowed(operator);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721A.sol';
    /**
     * @dev Interface of ERC721AQueryable.
     */
    interface IERC721AQueryable is IERC721A {
        /**
         * Invalid query range (`start` >= `stop`).
         */
        error InvalidQueryRange();
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view returns (uint256[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view returns (uint256[] memory);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721A.sol';
    /**
     * @dev Interface of ERC721 token receiver.
     */
    interface ERC721A__IERC721Receiver {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    /**
     * @title ERC721A
     *
     * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
     * Non-Fungible Token Standard, including the Metadata extension.
     * Optimized for lower gas during batch mints.
     *
     * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
     * starting from `_startTokenId()`.
     *
     * Assumptions:
     *
     * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
     * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
     */
    contract ERC721A is IERC721A {
        // Reference type for token approval.
        struct TokenApprovalRef {
            address value;
        }
        // =============================================================
        //                           CONSTANTS
        // =============================================================
        // Mask of an entry in packed address data.
        uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
        // The bit position of `numberMinted` in packed address data.
        uint256 private constant _BITPOS_NUMBER_MINTED = 64;
        // The bit position of `numberBurned` in packed address data.
        uint256 private constant _BITPOS_NUMBER_BURNED = 128;
        // The bit position of `aux` in packed address data.
        uint256 private constant _BITPOS_AUX = 192;
        // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
        uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
        // The bit position of `startTimestamp` in packed ownership.
        uint256 private constant _BITPOS_START_TIMESTAMP = 160;
        // The bit mask of the `burned` bit in packed ownership.
        uint256 private constant _BITMASK_BURNED = 1 << 224;
        // The bit position of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
        // The bit mask of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
        // The bit position of `extraData` in packed ownership.
        uint256 private constant _BITPOS_EXTRA_DATA = 232;
        // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
        uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
        // The mask of the lower 160 bits for addresses.
        uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
        // The maximum `quantity` that can be minted with {_mintERC2309}.
        // This limit is to prevent overflows on the address data entries.
        // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
        // is required to cause an overflow, which is unrealistic.
        uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
        // The `Transfer` event signature is given by:
        // `keccak256(bytes("Transfer(address,address,uint256)"))`.
        bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
            0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
        // =============================================================
        //                            STORAGE
        // =============================================================
        // The next token ID to be minted.
        uint256 private _currentIndex;
        // The number of tokens burned.
        uint256 private _burnCounter;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to ownership details
        // An empty struct value does not necessarily mean the token is unowned.
        // See {_packedOwnershipOf} implementation for details.
        //
        // Bits Layout:
        // - [0..159]   `addr`
        // - [160..223] `startTimestamp`
        // - [224]      `burned`
        // - [225]      `nextInitialized`
        // - [232..255] `extraData`
        mapping(uint256 => uint256) private _packedOwnerships;
        // Mapping owner address to address data.
        //
        // Bits Layout:
        // - [0..63]    `balance`
        // - [64..127]  `numberMinted`
        // - [128..191] `numberBurned`
        // - [192..255] `aux`
        mapping(address => uint256) private _packedAddressData;
        // Mapping from token ID to approved address.
        mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        // =============================================================
        //                          CONSTRUCTOR
        // =============================================================
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
            _currentIndex = _startTokenId();
        }
        // =============================================================
        //                   TOKEN COUNTING OPERATIONS
        // =============================================================
        /**
         * @dev Returns the starting token ID.
         * To change the starting token ID, please override this function.
         */
        function _startTokenId() internal view virtual returns (uint256) {
            return 0;
        }
        /**
         * @dev Returns the next token ID to be minted.
         */
        function _nextTokenId() internal view virtual returns (uint256) {
            return _currentIndex;
        }
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            // Counter underflow is impossible as _burnCounter cannot be incremented
            // more than `_currentIndex - _startTokenId()` times.
            unchecked {
                return _currentIndex - _burnCounter - _startTokenId();
            }
        }
        /**
         * @dev Returns the total amount of tokens minted in the contract.
         */
        function _totalMinted() internal view virtual returns (uint256) {
            // Counter underflow is impossible as `_currentIndex` does not decrement,
            // and it is initialized to `_startTokenId()`.
            unchecked {
                return _currentIndex - _startTokenId();
            }
        }
        /**
         * @dev Returns the total number of tokens burned.
         */
        function _totalBurned() internal view virtual returns (uint256) {
            return _burnCounter;
        }
        // =============================================================
        //                    ADDRESS DATA OPERATIONS
        // =============================================================
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            if (owner == address(0)) revert BalanceQueryForZeroAddress();
            return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
        }
        /**
         * Returns the number of tokens minted by `owner`.
         */
        function _numberMinted(address owner) internal view returns (uint256) {
            return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
        /**
         * Returns the number of tokens burned by or on behalf of `owner`.
         */
        function _numberBurned(address owner) internal view returns (uint256) {
            return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
        /**
         * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         */
        function _getAux(address owner) internal view returns (uint64) {
            return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
        }
        /**
         * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         * If there are multiple variables, please pack them into a uint64.
         */
        function _setAux(address owner, uint64 aux) internal virtual {
            uint256 packed = _packedAddressData[owner];
            uint256 auxCasted;
            // Cast `aux` with assembly to avoid redundant masking.
            assembly {
                auxCasted := aux
            }
            packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
            _packedAddressData[owner] = packed;
        }
        // =============================================================
        //                            IERC165
        // =============================================================
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            // The interface IDs are constants representing the first 4 bytes
            // of the XOR of all function selectors in the interface.
            // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
            // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
            return
                interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
        }
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
        /**
         * @dev Returns the token collection name.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
            string memory baseURI = _baseURI();
            return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
        }
        /**
         * @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, it can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return '';
        }
        // =============================================================
        //                     OWNERSHIPS OPERATIONS
        // =============================================================
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return address(uint160(_packedOwnershipOf(tokenId)));
        }
        /**
         * @dev Gas spent here starts off proportional to the maximum mint batch size.
         * It gradually moves to O(1) as tokens get transferred around over time.
         */
        function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnershipOf(tokenId));
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct at `index`.
         */
        function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnerships[index]);
        }
        /**
         * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
         */
        function _initializeOwnershipAt(uint256 index) internal virtual {
            if (_packedOwnerships[index] == 0) {
                _packedOwnerships[index] = _packedOwnershipOf(index);
            }
        }
        /**
         * Returns the packed ownership data of `tokenId`.
         */
        function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
            uint256 curr = tokenId;
            unchecked {
                if (_startTokenId() <= curr)
                    if (curr < _currentIndex) {
                        uint256 packed = _packedOwnerships[curr];
                        // If not burned.
                        if (packed & _BITMASK_BURNED == 0) {
                            // Invariant:
                            // There will always be an initialized ownership slot
                            // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                            // before an unintialized ownership slot
                            // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                            // Hence, `curr` will not underflow.
                            //
                            // We can directly compare the packed value.
                            // If the address is zero, packed will be zero.
                            while (packed == 0) {
                                packed = _packedOwnerships[--curr];
                            }
                            return packed;
                        }
                    }
            }
            revert OwnerQueryForNonexistentToken();
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
         */
        function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
            ownership.addr = address(uint160(packed));
            ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
            ownership.burned = packed & _BITMASK_BURNED != 0;
            ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
        }
        /**
         * @dev Packs ownership data into a single uint256.
         */
        function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
            }
        }
        /**
         * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
         */
        function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
            // For branchless setting of the `nextInitialized` flag.
            assembly {
                // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
            }
        }
        // =============================================================
        //                      APPROVAL OPERATIONS
        // =============================================================
        /**
         * @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) public virtual override {
            address owner = ownerOf(tokenId);
            if (_msgSenderERC721A() != owner)
                if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                    revert ApprovalCallerNotOwnerNorApproved();
                }
            _tokenApprovals[tokenId].value = to;
            emit Approval(owner, to, tokenId);
        }
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
            return _tokenApprovals[tokenId].value;
        }
        /**
         * @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) public virtual override {
            if (operator == _msgSenderERC721A()) revert ApproveToCaller();
            _operatorApprovals[_msgSenderERC721A()][operator] = approved;
            emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
        }
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @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. See {_mint}.
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return
                _startTokenId() <= tokenId &&
                tokenId < _currentIndex && // If within bounds,
                _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
        }
        /**
         * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
         */
        function _isSenderApprovedOrOwner(
            address approvedAddress,
            address owner,
            address msgSender
        ) private pure returns (bool result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                msgSender := and(msgSender, _BITMASK_ADDRESS)
                // `msgSender == owner || msgSender == approvedAddress`.
                result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
            }
        }
        /**
         * @dev Returns the storage slot and value for the approved address of `tokenId`.
         */
        function _getApprovedSlotAndAddress(uint256 tokenId)
            private
            view
            returns (uint256 approvedAddressSlot, address approvedAddress)
        {
            TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
            // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId]`.
            assembly {
                approvedAddressSlot := tokenApproval.slot
                approvedAddress := sload(approvedAddressSlot)
            }
        }
        // =============================================================
        //                      TRANSFER OPERATIONS
        // =============================================================
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * 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
        ) public virtual override {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            if (to == address(0)) revert TransferToZeroAddress();
            _beforeTokenTransfers(from, to, tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // We can directly increment and decrement the balances.
                --_packedAddressData[from]; // Updates: `balance -= 1`.
                ++_packedAddressData[to]; // Updates: `balance += 1`.
                // Updates:
                // - `address` to the next owner.
                // - `startTimestamp` to the timestamp of transfering.
                // - `burned` to `false`.
                // - `nextInitialized` to `true`.
                _packedOwnerships[tokenId] = _packOwnershipData(
                    to,
                    _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                );
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (_packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != _currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, to, tokenId);
            _afterTokenTransfers(from, to, tokenId, 1);
        }
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, '');
        }
        /**
         * @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 memory _data
        ) public virtual override {
            transferFrom(from, to, tokenId);
            if (to.code.length != 0)
                if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
        }
        /**
         * @dev Hook that is called before a set of serially-ordered token IDs
         * are about to be transferred. This includes minting.
         * And also called before burning one token.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * 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, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _beforeTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Hook that is called after a set of serially-ordered token IDs
         * have been transferred. This includes minting.
         * And also called after one token has been burned.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
         * transferred to `to`.
         * - When `from` is zero, `tokenId` has been minted for `to`.
         * - When `to` is zero, `tokenId` has been burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
         *
         * `from` - Previous owner of the given token ID.
         * `to` - Target address that will receive the token.
         * `tokenId` - Token ID to be transferred.
         * `_data` - Optional data to send along with the call.
         *
         * Returns whether the call correctly returned the expected magic value.
         */
        function _checkContractOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
                bytes4 retval
            ) {
                return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert TransferToNonERC721ReceiverImplementer();
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
        // =============================================================
        //                        MINT OPERATIONS
        // =============================================================
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mint(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = _currentIndex;
            if (quantity == 0) revert MintZeroQuantity();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are incredibly unrealistic.
            // `balance` and `numberMinted` have a maximum limit of 2**64.
            // `tokenId` has a maximum limit of 2**256.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                _packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                uint256 toMasked;
                uint256 end = startTokenId + quantity;
                // Use assembly to loop and emit the `Transfer` event for gas savings.
                assembly {
                    // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                    toMasked := and(to, _BITMASK_ADDRESS)
                    // Emit the `Transfer` event.
                    log4(
                        0, // Start of data (0, since no data).
                        0, // End of data (0, since no data).
                        _TRANSFER_EVENT_SIGNATURE, // Signature.
                        0, // `address(0)`.
                        toMasked, // `to`.
                        startTokenId // `tokenId`.
                    )
                    for {
                        let tokenId := add(startTokenId, 1)
                    } iszero(eq(tokenId, end)) {
                        tokenId := add(tokenId, 1)
                    } {
                        // Emit the `Transfer` event. Similar to above.
                        log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                    }
                }
                if (toMasked == 0) revert MintToZeroAddress();
                _currentIndex = end;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * This function is intended for efficient minting only during contract creation.
         *
         * It emits only one {ConsecutiveTransfer} as defined in
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
         * instead of a sequence of {Transfer} event(s).
         *
         * Calling this function outside of contract creation WILL make your contract
         * non-compliant with the ERC721 standard.
         * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
         * {ConsecutiveTransfer} event is only permissible during contract creation.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {ConsecutiveTransfer} event.
         */
        function _mintERC2309(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = _currentIndex;
            if (to == address(0)) revert MintToZeroAddress();
            if (quantity == 0) revert MintZeroQuantity();
            if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                _packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                _currentIndex = startTokenId + quantity;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
        /**
         * @dev Safely mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
         * - `quantity` must be greater than 0.
         *
         * See {_mint}.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _safeMint(
            address to,
            uint256 quantity,
            bytes memory _data
        ) internal virtual {
            _mint(to, quantity);
            unchecked {
                if (to.code.length != 0) {
                    uint256 end = _currentIndex;
                    uint256 index = end - quantity;
                    do {
                        if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                            revert TransferToNonERC721ReceiverImplementer();
                        }
                    } while (index < end);
                    // Reentrancy protection.
                    if (_currentIndex != end) revert();
                }
            }
        }
        /**
         * @dev Equivalent to `_safeMint(to, quantity, '')`.
         */
        function _safeMint(address to, uint256 quantity) internal virtual {
            _safeMint(to, quantity, '');
        }
        // =============================================================
        //                        BURN OPERATIONS
        // =============================================================
        /**
         * @dev Equivalent to `_burn(tokenId, false)`.
         */
        function _burn(uint256 tokenId) internal virtual {
            _burn(tokenId, false);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            address from = address(uint160(prevOwnershipPacked));
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            if (approvalCheck) {
                // The nested ifs save around 20+ gas over a compound boolean condition.
                if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                    if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            }
            _beforeTokenTransfers(from, address(0), tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // Updates:
                // - `balance -= 1`.
                // - `numberBurned += 1`.
                //
                // We can directly decrement the balance, and increment the number burned.
                // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
                // Updates:
                // - `address` to the last owner.
                // - `startTimestamp` to the timestamp of burning.
                // - `burned` to `true`.
                // - `nextInitialized` to `true`.
                _packedOwnerships[tokenId] = _packOwnershipData(
                    from,
                    (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                );
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (_packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != _currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, address(0), tokenId);
            _afterTokenTransfers(from, address(0), tokenId, 1);
            // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
            unchecked {
                _burnCounter++;
            }
        }
        // =============================================================
        //                     EXTRA DATA OPERATIONS
        // =============================================================
        /**
         * @dev Directly sets the extra data for the ownership data `index`.
         */
        function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
            uint256 packed = _packedOwnerships[index];
            if (packed == 0) revert OwnershipNotInitializedForExtraData();
            uint256 extraDataCasted;
            // Cast `extraData` with assembly to avoid redundant masking.
            assembly {
                extraDataCasted := extraData
            }
            packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
            _packedOwnerships[index] = packed;
        }
        /**
         * @dev Called during each token transfer to set the 24bit `extraData` field.
         * Intended to be overridden by the cosumer contract.
         *
         * `previousExtraData` - the value of `extraData` before transfer.
         *
         * 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, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _extraData(
            address from,
            address to,
            uint24 previousExtraData
        ) internal view virtual returns (uint24) {}
        /**
         * @dev Returns the next extra data for the packed ownership data.
         * The returned result is shifted into position.
         */
        function _nextExtraData(
            address from,
            address to,
            uint256 prevOwnershipPacked
        ) private view returns (uint256) {
            uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
            return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
        }
        // =============================================================
        //                       OTHER OPERATIONS
        // =============================================================
        /**
         * @dev Returns the message sender (defaults to `msg.sender`).
         *
         * If you are writing GSN compatible contracts, you need to override this function.
         */
        function _msgSenderERC721A() internal view virtual returns (address) {
            return msg.sender;
        }
        /**
         * @dev Converts a uint256 to its ASCII string decimal representation.
         */
        function _toString(uint256 value) internal pure virtual returns (string memory str) {
            assembly {
                // The maximum value of a uint256 contains 78 digits (1 byte per digit),
                // but we allocate 0x80 bytes to keep the free memory pointer 32-byte word aliged.
                // We will need 1 32-byte word to store the length,
                // and 3 32-byte words to store a maximum of 78 digits. Total: 0x20 + 3 * 0x20 = 0x80.
                str := add(mload(0x40), 0x80)
                // Update the free memory pointer to allocate.
                mstore(0x40, str)
                // Cache the end of the memory to calculate the length later.
                let end := str
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                // prettier-ignore
                for { let temp := value } 1 {} {
                    str := sub(str, 1)
                    // Write the character to the pointer.
                    // The ASCII index of the '0' character is 48.
                    mstore8(str, add(48, mod(temp, 10)))
                    // Keep dividing `temp` until zero.
                    temp := div(temp, 10)
                    // prettier-ignore
                    if iszero(temp) { break }
                }
                let length := sub(end, str)
                // Move the pointer 32 bytes leftwards to make room for the length.
                str := sub(str, 0x20)
                // Store the length.
                mstore(str, length)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    /**
     * @dev Interface of ERC721A.
     */
    interface IERC721A {
        /**
         * The caller must own the token or be an approved operator.
         */
        error ApprovalCallerNotOwnerNorApproved();
        /**
         * The token does not exist.
         */
        error ApprovalQueryForNonexistentToken();
        /**
         * The caller cannot approve to their own address.
         */
        error ApproveToCaller();
        /**
         * Cannot query the balance for the zero address.
         */
        error BalanceQueryForZeroAddress();
        /**
         * Cannot mint to the zero address.
         */
        error MintToZeroAddress();
        /**
         * The quantity of tokens minted must be more than zero.
         */
        error MintZeroQuantity();
        /**
         * The token does not exist.
         */
        error OwnerQueryForNonexistentToken();
        /**
         * The caller must own the token or be an approved operator.
         */
        error TransferCallerNotOwnerNorApproved();
        /**
         * The token must be owned by `from`.
         */
        error TransferFromIncorrectOwner();
        /**
         * Cannot safely transfer to a contract that does not implement the
         * ERC721Receiver interface.
         */
        error TransferToNonERC721ReceiverImplementer();
        /**
         * Cannot transfer to the zero address.
         */
        error TransferToZeroAddress();
        /**
         * The token does not exist.
         */
        error URIQueryForNonexistentToken();
        /**
         * The `quantity` minted with ERC2309 exceeds the safety limit.
         */
        error MintERC2309QuantityExceedsLimit();
        /**
         * The `extraData` cannot be set on an unintialized ownership slot.
         */
        error OwnershipNotInitializedForExtraData();
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct TokenOwnership {
            // The address of the owner.
            address addr;
            // Stores the start time of ownership with minimal overhead for tokenomics.
            uint64 startTimestamp;
            // Whether the token has been burned.
            bool burned;
            // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
            uint24 extraData;
        }
        // =============================================================
        //                         TOKEN COUNTERS
        // =============================================================
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() external view returns (uint256);
        // =============================================================
        //                            IERC165
        // =============================================================
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
        // =============================================================
        //                            IERC721
        // =============================================================
        /**
         * @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`,
         * 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 be 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,
            bytes calldata data
        ) external;
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` 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);
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
        /**
         * @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);
        // =============================================================
        //                           IERC2309
        // =============================================================
        /**
         * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
         * (inclusive) is transferred from `from` to `to`, as defined in the
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
         *
         * See {_mintERC2309} for more details.
         */
        event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    interface IOperatorFilterRegistry {
        function isOperatorAllowed(address registrant, address operator) external view returns (bool);
        function register(address registrant) external;
        function registerAndSubscribe(address registrant, address subscription) external;
        function registerAndCopyEntries(address registrant, address registrantToCopy) external;
        function unregister(address addr) external;
        function updateOperator(address registrant, address operator, bool filtered) external;
        function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
        function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
        function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
        function subscribe(address registrant, address registrantToSubscribe) external;
        function unsubscribe(address registrant, bool copyExistingEntries) external;
        function subscriptionOf(address addr) external returns (address registrant);
        function subscribers(address registrant) external returns (address[] memory);
        function subscriberAt(address registrant, uint256 index) external returns (address);
        function copyEntriesOf(address registrant, address registrantToCopy) external;
        function isOperatorFiltered(address registrant, address operator) external returns (bool);
        function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
        function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
        function filteredOperators(address addr) external returns (address[] memory);
        function filteredCodeHashes(address addr) external returns (bytes32[] memory);
        function filteredOperatorAt(address registrant, uint256 index) external returns (address);
        function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
        function isRegistered(address addr) external returns (bool);
        function codeHashOf(address addr) external returns (bytes32);
    }
    

    File 2 of 2: MintPassTwo
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.9;
    import "./PvSignedAllowlist.sol";
    import './AbstractERC1155Factory.sol';
    /*
    * @title ERC1155 token for MintPass #2
    *
    * @author Niftydude
    */
    contract MintPassTwo is PvSignedAllowlist, AbstractERC1155Factory {
        uint256 constant MAX_SUPPLY = 203282;
        uint256 public windowOpens;
        uint256 public windowCloses;
        address redeemContract;
        bool redeemContractFinalized;
        bool redeemEnabled;
        bool mintingClosed;
        constructor(
            string memory _name, 
            string memory _symbol,  
            string memory _uri,
            uint256 _windowOpens,
            uint256 _windowCloses
        ) ERC1155(_uri) {
            name_ = _name;
            symbol_ = _symbol;
            windowOpens = _windowOpens;
            windowCloses = _windowCloses;
            _setSigner(msg.sender);
            _setTicketSupply(MAX_SUPPLY);
            _mint(msg.sender, 0, 10000, "");
        } 
        function mint(
            bytes calldata _signature, 
            uint256 _ticketId,
            uint256 _amount
        ) external {
            require(totalSupply(0) + _amount <= MAX_SUPPLY, "Max supply reached");
            require (block.timestamp > windowOpens && block.timestamp < windowCloses, "Window closed");
            require(!mintingClosed, "minting is closed");
            _verify(_signature, _ticketId, _amount);
            _invalidate(_ticketId);
            _mint(msg.sender, 0, _amount, "");
        } 
        function ownerMint (
            address[] calldata _to, 
            uint256[] calldata _amount
        ) external onlyOwner {
            require(_to.length == _amount.length, "same length required");
            require(!mintingClosed, "minting is closed");
            for(uint256 i; i < _to.length; i++) {
                require(totalSupply(0) + _amount[i] <= MAX_SUPPLY, "Max supply reached");
                _mint(_to[i], 0, _amount[i], "");
            }
        }        
        function burnFromRedeem(
            address _account, 
            uint256 _amount
        ) external {
            require(redeemContract == msg.sender, "Burnable: Only allowed from redeemable contract");
            require(redeemEnabled, "burn from redeem disabled");
            _burn(_account, 0, _amount);
        }  
        function editWindows(
            uint256 _windowOpens, 
            uint256 _windowCloses
        ) external onlyOwner {
            require(_windowOpens < _windowCloses, "open window must be before close window");
            windowOpens = _windowOpens;
            windowCloses = _windowCloses;
        }     
        function finalizeRedeemContract(
            address _redeemContract
        ) external onlyOwner {
            require(!redeemContractFinalized, "contract is finalized");
            redeemContract = _redeemContract;  
            redeemContractFinalized = true;
        } 
        function setURI(
            string memory _baseURI
        ) external onlyOwner {
            _setURI(_baseURI);
        }    
        function toggleRedeem() external onlyOwner {
            redeemEnabled = !redeemEnabled;
        }    
        function closeMintingForever() external onlyOwner {
            mintingClosed = true;
        }   
        function invalidateTickets(
            uint256[] calldata _ticketIds
        ) external onlyOwner {
            for(uint256 i; i < _ticketIds.length; i++) {
                 _invalidate(_ticketIds[i]);
            }
        }
        function setSigner(
            address _signer
        ) external onlyOwner {
            _setSigner(_signer);
        }
        function resetWithNewSupply(
            uint256 _supply
        ) external onlyOwner {
            _setTicketSupply(_supply);
        }    
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.9;
    import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    contract PvSignedAllowlist {
        using ECDSA for bytes32;
        uint256 private constant UINT256_MAX = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
        uint256[] ticketBatches;
        address signer;
        function _verify(
            bytes calldata _signature, 
            uint256 _ticketId, 
            uint256 _amount
        ) internal view {
            bytes32 hash = keccak256(abi.encodePacked(msg.sender, _ticketId, _amount));
            require(hash.toEthSignedMessageHash().recover(_signature) == signer, "signature invalid");
        }
        function _invalidate(uint256 _ticketId) internal {
            require(_ticketId < ticketBatches.length * 256, "ticket does not exist");
            uint256 batchId = _ticketId / 256;
            uint256 ticketIdInBatch = _ticketId % 256;
            require((ticketBatches[batchId] >> ticketIdInBatch) & uint256(1) != 0, "ticket already used");
            ticketBatches[batchId] = ticketBatches[batchId] & ~(uint256(1) << ticketIdInBatch);       
        }
        function isEligible(uint256 _ticketId) public view returns (bool) {
            require(_ticketId < ticketBatches.length * 256, "ticket does not exist");
            return (ticketBatches[_ticketId / 256] >> _ticketId % 256) & uint256(1) == 1;
        }
        function _setTicketSupply(uint256 supply) internal {
            uint256 batchAmount = (supply / 256) + 1;
            uint256[] memory newBatchArray = new uint256[](batchAmount);
            for (uint256 i; i < batchAmount; i++) {
                newBatchArray[i] = UINT256_MAX;
            }
            ticketBatches = newBatchArray;
        }
        function _setSigner(address _signer) internal {
            signer = _signer;
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.9;
    import '@openzeppelin/contracts/access/Ownable.sol';
    import '@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol';
    import '@openzeppelin/contracts/security/Pausable.sol';
    import '@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol';
    abstract contract AbstractERC1155Factory is ERC1155Supply, ERC1155Burnable, Ownable {
        
        string public name_;
        string public symbol_;   
        function name() public view returns (string memory) {
            return name_;
        }
        function symbol() public view returns (string memory) {
            return symbol_;
        }          
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual override(ERC1155, ERC1155Supply) {
            super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
        } 
    }// SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            } else if (error == RecoverError.InvalidSignatureV) {
                revert("ECDSA: invalid signature 'v' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            // Check the signature length
            // - case 65: r,s,v signature (standard)
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else if (signature.length == 64) {
                bytes32 r;
                bytes32 vs;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    vs := mload(add(signature, 0x40))
                }
                return tryRecover(hash, r, vs);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address, RecoverError) {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            if (v != 27 && v != 28) {
                return (address(0), RecoverError.InvalidSignatureV);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (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 Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol)
    pragma solidity ^0.8.0;
    import "../ERC1155.sol";
    /**
     * @dev Extension of {ERC1155} that allows token holders to destroy both their
     * own tokens and those that they have been approved to use.
     *
     * _Available since v3.1._
     */
    abstract contract ERC1155Burnable is ERC1155 {
        function burn(
            address account,
            uint256 id,
            uint256 value
        ) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burn(account, id, value);
        }
        function burnBatch(
            address account,
            uint256[] memory ids,
            uint256[] memory values
        ) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burnBatch(account, ids, values);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Supply.sol)
    pragma solidity ^0.8.0;
    import "../ERC1155.sol";
    /**
     * @dev Extension of ERC1155 that adds tracking of total supply per id.
     *
     * Useful for scenarios where Fungible and Non-fungible tokens have to be
     * clearly identified. Note: While a totalSupply of 1 might mean the
     * corresponding is an NFT, there is no guarantees that no other token with the
     * same id are not going to be minted.
     */
    abstract contract ERC1155Supply is ERC1155 {
        mapping(uint256 => uint256) private _totalSupply;
        /**
         * @dev Total amount of tokens in with a given id.
         */
        function totalSupply(uint256 id) public view virtual returns (uint256) {
            return _totalSupply[id];
        }
        /**
         * @dev Indicates whether any token exist with a given id, or not.
         */
        function exists(uint256 id) public view virtual returns (bool) {
            return ERC1155Supply.totalSupply(id) > 0;
        }
        /**
         * @dev See {ERC1155-_beforeTokenTransfer}.
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual override {
            super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
            if (from == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    _totalSupply[ids[i]] += amounts[i];
                }
            }
            if (to == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    _totalSupply[ids[i]] -= amounts[i];
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/ERC1155.sol)
    pragma solidity ^0.8.0;
    import "./IERC1155.sol";
    import "./IERC1155Receiver.sol";
    import "./extensions/IERC1155MetadataURI.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of the basic standard multi-token.
     * See https://eips.ethereum.org/EIPS/eip-1155
     * Originally based on code by Enjin: https://github.com/enjin/erc-1155
     *
     * _Available since v3.1._
     */
    contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using Address for address;
        // Mapping from token ID to account balances
        mapping(uint256 => mapping(address => uint256)) private _balances;
        // Mapping from account to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
        string private _uri;
        /**
         * @dev See {_setURI}.
         */
        constructor(string memory uri_) {
            _setURI(uri_);
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) public view virtual override returns (string memory) {
            return _uri;
        }
        /**
         * @dev See {IERC1155-balanceOf}.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: balance query for the zero address");
            return _balances[id][account];
        }
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
            public
            view
            virtual
            override
            returns (uint256[] memory)
        {
            require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
            uint256[] memory batchBalances = new uint256[](accounts.length);
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts[i], ids[i]);
            }
            return batchBalances;
        }
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[account][operator];
        }
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _safeTransferFrom(from, to, id, amount, data);
        }
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: transfer caller is not owner nor approved"
            );
            _safeBatchTransferFrom(from, to, ids, amounts, data);
        }
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: transfer to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
            emit TransferSingle(operator, from, to, id, amount);
            _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            require(to != address(0), "ERC1155: transfer to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
                _balances[id][to] += amount;
            }
            emit TransferBatch(operator, from, to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
        }
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function _setURI(string memory newuri) internal virtual {
            _uri = newuri;
        }
        /**
         * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);
            _balances[id][to] += amount;
            emit TransferSingle(operator, address(0), to, id, amount);
            _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _mintBatch(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; i++) {
                _balances[ids[i]][to] += amounts[i];
            }
            emit TransferBatch(operator, address(0), to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
        }
        /**
         * @dev Destroys `amount` tokens of token type `id` from `from`
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `from` must have at least `amount` tokens of token type `id`.
         */
        function _burn(
            address from,
            uint256 id,
            uint256 amount
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            emit TransferSingle(operator, from, address(0), id, amount);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         */
        function _burnBatch(
            address from,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
            }
            emit TransferBatch(operator, from, address(0), ids, amounts);
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits a {ApprovalForAll} event.
         */
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC1155: setting approval status for self");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `id` and `amount` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
            uint256[] memory array = new uint256[](1);
            array[0] = element;
            return array;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (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 be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) external;
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
    pragma solidity ^0.8.0;
    import "../IERC1155.sol";
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\\{id\\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (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
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (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);
    }