// File: sandman.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

interface SandInterface {
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function tokenOfOwnerByIndex(address owner, uint256 index)
        external
        view
        returns (uint256 tokenId);
}

contract SandAvatars is ERC721, ERC721Enumerable, Ownable {
    bool public saleIsActive = false;
    bool public burned = false;
    address public SandAddress = 0xB90b34B41658aC20A0051874FeeE1933c3614fa7;
    SandInterface SandContract = SandInterface(SandAddress);
    
    constructor() ERC721("SandAvatars", "SNAV") {
    }
     

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
    
    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }
    
    function toHashCode(uint256 value) internal pure returns (string memory) {
        uint256 i;

        bytes memory buffer = "000000";
        for(i=6;i>0;i--) {
            if (value % 16 < 10)
                buffer[i-1] = bytes1(uint8(48 + uint256(value % 16)));
            else
                buffer[i-1] = bytes1(uint8(55 + uint256(value % 16)));

            value /= 16;
        }
        return string(buffer);
    }
    
    

    function toString(uint256 value) internal pure returns (string memory) {
        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);
    }
    
    function getPolygon(uint256 num, string memory color, string memory op) internal pure returns (string memory) {
        uint256[207]memory xtypes =[
            14121436260491623888573569494265958210382088460059485271557129702735745326,
            3379687223624639408404345647054286820230943047769096274235841555659165627614,
            4766529846908783929200657017927412346891862284447113714675691880753314010650,
            2414848826299563751631584,
            2415403168366050671711990,
            165989458421190421426294049862974048,
            2990041845436834032465436684735961822206490428694177,
            205519767453372228188215768215336195851890616474781117116470017,
            783342371240660400615497362662867695169835591505009495153,
            3015605336097770010832060948773439899320263180355111271447081940574438532253,
            1699410383171683060484640923082407994735009912351540698902523680158858963667,
            2067439483755009076537239878613967120688171739225650558832839415147437291640,
            2110285635025633850782162448438909787314249243868635101044081548977791378060,
            1813537826634847775588271020849619009466237369690820983365919405283580390552,
            1433165981178011733008463800384150766019695303333840710976987787175893725835,
            1347834300580146900593882219877353796923237396140869764780512560971696623306,
            1588291142145332360308884532736805401102486146533600217899801854658095932103,
            1630640689404733378642505262896486732354314158877471864006247217340780175548,
            1926975200976366162727790765948535744022368363806555952276809889580033045183,
            2195729526611169200073088264115405134266050171528327914526689757427595025581,
            2266183416249769109687683253195421600011544355208010500246994655874713467054,
            1942021118171089200927872160880968143467387698704835635209143544230560220841,
            2026692120635480416902954766191778704951595844413918284067846871351766098126,
            2252931742901068372445672611749832774842961514843407512141469648329648975563,
            2295116187662235110121913666276726987468444668928175066731688914204036246718,
            2592194794286804437698157144159526339621523043225693230464121704378048799436,
            2535270053559044811589912991644676175666664223951765667507672669824291794632,
            2676810635330829446627987247531416732227338364894945326305024218158728837315,
            2676507767503271220336019071355421318369328191959582692781556145107637730497,
            2860260185232810836874932161100098492177796172019734599621174229889736083643,
            2704583530546838355578889545793450502804454913237751059045251025304634622638,
            2732605051757189469738771911402128307348469092831311049021474868234958504110,
            2689896882341260570152089198804438177341639211772304451963188924944556062881,
            2874339369183394173601732921210730487262146816407635719706621996970437346462,
            134063278,
            11401402070285144550100871971896250052146431669,
            11401442479798540347354564667579758730284697744,
            2010405393385468769754390500839040943260313875155174100691674881837603800274,
            53852718675364590822474638051961946119679634989037219218611160268927,
            2873152535253068203115307214512935299514021674337530122812293448446001082573,
            632967951439172729344733328545,
            2703590214691199062276704278272662481619631230524746364075007852563519075527,
            3142100272699584422061276135492595947566116804089604574541184736671832567449,
            2989437538194262125706135904879775746659906037333659,
            2732882416327165001845161696611165857660612391574224505373154795372861555376,
            511,
            2407752739435277468182135965268740961167067564356323244993211865623229981385,
            2718635756548816077094781045246157570894954467225593574078410329624519202986,
            205426318550813624678543263541887184093338878840179569528765611,
            783755749980388114187626910565273421423263977749007664810,
            2548935505955974898918714072113448954440083838302132660405920669118189609678,
            2620798165283744002906637766862406499463773050371800692306162182138139601596,
            511,
            3072227530827800386450195294804395741003464330979216039104110948505046855347,
            134062774,
            2492699542259379918880031894535889655400300980491640244623151733365212783310,
            3439125500424588101080488050237853159581039880613797914734533628447204922543,
            35140538584256,
            3242094284692887871609116300080036303492460653890804351075462070651154177202,
            511,
            1770013146521665037408692954108461375963030202334087742099350891873277133542,
            2416417129151844328456352,
            205354787381561677649152221282543919859758865255427095704153723,
            4554618901733828934554583591157904263166577235187268654483446904656196608231,
            205399677480996743294400265142671549319989067471689792870214765,
            1687649379406570998776343934026219620523553877468665929082612794343857930982,
            35146584669401,
            43490082804405313463985993646168051869873,
            682778994860606022295232778088851114906462078344796921178050393667028676812,
            924784587014537063152182727386123816934642400149158636683653472794560994908,
            511,
            740975772651252457144836420278673397610254157186597222250099236134980751977,
            2414117941576320369311410,
            632708923678708594065717615261,
            1458925500838540507418926026561993567301703685253672786264170589214896329935,
            1362215544816844226291935252053426049015218768525540613182698930612617390203,
            205358079224472907756417989573029084098844703882026101090076869,
            783366263800606206590835024970459678702881525236644753005,
            11400184790511075841139148509544386174780755664,
            165862180158385183703562576710887618,
            1953753321857745697879799949839981476218506366288294591805006875595925160150,
            9209770061908476534,
            9208258027669985499,
            1007024817493647344034072868464098635223782697202574437333574124131465880276,
            978311935401717486461471199063926706147737078905916884262441267563763117176,
            1744463536117297771199500556841754719200382694378654872668579678780200630368,
            511,
            1744601465411721296895678703119841194368939717120234214930076101190129364598,
            11401222567826577545303997980370987807270047450,
            9208989541913880222,
            1092826092546994839469209809358617178552720724428088672892094552082176036544,
            632684934038491608715134540950,
            14115595813093446524490909446834395552161017956518832857934551862262970508,
            632986958806261141854888479370,
            632977282640443796338332746377,
            2052946140791500114938153887393848933442548125195317854590544344108166871666,
            134024309,
            2414284504605938271190647,
            2414432961303085847740591,
            1840619951669604201410879954770232798770635410344762643704857699299414045884,
            2054740440345882338413182726751774799923504795098535273396345647260901121653,
            11401099814810876640669013063882522770837151408,
            2251164459629472805890764632980280277423766955907349854903606843989721679987,
            35133399578745,
            632965160288698604399670134412,
            632963006881331930588495022277,
            1786344573953401262130609349327547713072556579231105641242862738580471876815,
            2038589805006897792509112007954350735573250959223795959103614935773248814209,
            53842228122476702495773295789384926848390152018530179498957340490361,
            2111666526756141710963288250158183316620790909592475932262883279353406879950,
            134025936,
            165907096476797172510820836217519220,
            165907184370672188268528709397970124,
            1658082137457338989675227097692720737868293871022172673441890803106526591693,
            1883024388535656625284391916361025676073851550858069989271493020769645815925,
            1926920472707949547012838945053378431215376612467398300984843403947283124847,
            35131542532261,
            2024594198579077053330764490684944254136592945947480819607862982763103974004,
            35131923427956,
            165909021475966603241960532792900752,
            165909083373204686735841121470840002,
            1885618862360980679350640322437473822248060842137738006611198105431721960658,
            1742173066023287913099403968227517944924073894310750451819876872377331939012,
            1614407666257820487363169021269399758278734125660509710128450889567286921848,
            1744464615782850979964182648476765026166962618378931230221541869862448329839,
            134014162,
            11401182764097204976827754137621811304469167267,
            1771877757361298981460197733359093436404203289488771558972277815195612811459,
            35133957411001,
            2010266548816189564479104322685517328153080461434873302541240831931155540179,
            1304683555199795152622005937938311831570780363298831092900867642675445952117,
            1969021876408794075999015047652559943941859790002927827734170938252782778009,
            43490414745042222856303808090815535583909,
            2180572158151246410001024254246908758834719272846442894189196745760436466396,
            1673403802771746401375198818222556837670706048123256272574605586619058958977,
            1885288008633175329315918003045024399639454063428632155870688280191534429897,
            1416905731959046147692107590348875820999182264648841806339526292113423404216,
            1997291483260239039461852812345115693394643947621579986309362922487406451834,
            783558446483948923466464176747535046874779770473507527338,
            14115978838654814715707104984262644478936595142475981459604830162147428033,
            633107969808034801031089073827,
            2633578470602599440322905773949466446156875479267007458201540814813565895350,
            632977495378512133649644484776,
            165929986036551556547801149694623415,
            2989025485403311877817610573034810975450514052426906,
            205407434988195293878750642328847735295359291050132879493903533,
            11404056456687223639749243846043887237610963095,
            2989675086510346121999155662963669593588724326309475,
            43500872086127607303759630063956263790259,
            43501704123066862598939503992370536738999,
            11405895771622953147234522950227641433617192204,
            633135015023432504038451692802,
            1798132692690596179620705189163856188119368407084938759745728243059276205742,
            2989220037414416967977113123657014036013879774232178,
            165937578388141905202686021359195820,
            2414727696459509174196890,
            2414691127125256287900332,
            165915365962175250832669694316052105,
            165910991072664731647969066886692541,
            165915364714563768096814076148908681,
            632905030222108565907283182269,
            14120708005514235896398854230464658906875090178430929295545649270898687532,
            3337199960013931773256446509889229036480433598974937326816702961831104073785,
            3605596203950561889636721869696093579010718870466811845253629283825091211308,
            633155812478473693991465778734,
            9213733475672780801,
            3167748284762293053715838884979026074229237355454641984176617526858699095095,
            9213067443423788601,
            205469983996266401852074125388687431408097597551877715399396883,
            3351113016766149477002540489062003105409306561172428804040580041473100728372,
            9213383966672673328,
            43512465521962263479832723055980780052022,
            3520125028586159092693396574728389547239517587975760695831987336475883987511,
            3534756679028325853283430757339694332274937217738686833232855861131381961745,
            633170324347821347278826632744,
            3576139438807138707926215165472039742119299231360549960477337344216086985298,
            9213737944048074266,
            14119204143756774178704498797963472268374854012663999297240300317580833876,
            2415408463281380994969144,
            3365166644055540638233802210399874078674695627002040277764271603049931312166,
            165988148429214389023346425638543400,
            1887746225632730957138348290686608844568954207126319086712163562729765690710,
            1915600863297605383381527809737518613446657499530049954771583968952018611994,
            2113322577499690111671980382294985200037260660369180400997101761556814565131,
            2296689143527392781842689264020699383608121252820658176192302688260167906561,
            1660566289916744597573142969784638710355914531095542918433558190781161095928,
            2534082095414892391686150036943765103020588807891018485576253045401890385141,
            633024223136024389052689114166,
            2241391978884043179921934352810270624406243989287534804133301471739814963539,
            14116875255364126381735783362239064288396228655148828240929468015891006244,
            2193355749223663387186473805456239874894208331871277368480859777693891256633,
            2403831831909932078863419000029126387693398488022480060606911847939131651161,
            632995175865805100776593642529,
            1995274819321270135644234375868044978329361002748554918878840054020430981452,
            2375396906030561049849552299865999851499367560653426663546240001308245435983,
            35139812415002,
            2235677420740099074517491835994945260329291511134955606995441301360674695509,
            14116911062155530733820699789705328612116920974874452489152294027973179472,
            2678770361965612704120099657034866152680834834755057279760189508348235082564,
            3287203733699690768233314741557537574339684689442054036487386010876555637992,
            4064839512598947773653950189688692616236907353206181272213310648298450050346,
            4120828199034495056757676173705786195371723329918666661524593207136859074328,
            2415394844344362282141479,

            36325149170812693104873872163691011689991992041050311830217183986701738246657,
            64478519805822442329262443918803540875483425373278030259456266971945429652305,
            86726949164196919860061327850596874826858820280533989768291036578610410590608,
            4291282370
            ];
            
            
        uint256 i;
        uint256 first_index;
        uint256 cur_num;
        uint256 k;
        uint256 pos;
        uint256 part;
        string memory res;
        uint256 result;
        
        part = (xtypes[203+(num/32)] / (256**(num%32)));
        first_index = part%256;
        res = '';
    
        for(i = first_index-1; i<=250; i++) {
            if (xtypes[i] == 0)
                break;
    
            cur_num= xtypes[i];    
            k=0;
            for(pos=0;pos<=27;pos++) {
                result = (cur_num /  (512 ** pos)) % 512;
                if (result == 511)
                    break;
    
                if (k%2 == 0) {
                        res = string(abi.encodePacked(res, toString(result*3), ','));

                }
                else {
                    res = string(abi.encodePacked(res, toString(result*3), ' '));

                }
                k++;
             }   
            if (result == 511)
                break;
        }

        return string(abi.encodePacked('<polygon opacity="',op,'" points="', res, '" fill="#',color,'" />'));
    
    }
    
    function tokenURI(uint256 tokenId) pure public override(ERC721)  returns (string memory) {
        uint256[19] memory xtypes;
        string[6] memory colors;
        string memory output;

        uint256[12] memory params;

        uint256 pos;
        uint256 i;

        uint256 rand = random(string(abi.encodePacked('SandMan',toString(tokenId))));

        params[0] = 1 + (rand % 30); // pallette=
        params[1] = 1 + ((rand/100) % 5);// beard
        params[2] = 1 + ((rand/1000) % 6); // cap
        params[3] = 1 + ((rand/10000) % 5); // glasses

        params[4] = 1 + ((rand/100000) % 4); // tube
        params[5] = 1 + ((rand/1000000) % 6); // object
        params[6] = 1 + ((rand/100000000) % 6); // far
        params[7] = 1 + ((rand/1000000000) % 10); // Band - aid



        xtypes[0] = 1765300933158159279126365688755849989787469680610145113308712138382768895;
        xtypes[1] = 1764921079952145254276332071479222557911241288448461372909069781721677547;
        xtypes[2] = 1501679087584470838161589003975082910140105054122807965032669773169688478;
        xtypes[3] = 1764781974038155230893998203428071971942131358905837942209321033418145694;
        xtypes[4] = 1764351442057709576512408600184270815014492966481600074584100303563390699;
        xtypes[5] = 1764513942681239813290380170455237895030536670527749966108226146279030779;
        xtypes[6] = 1765083764856076636672938774629519978791146014135139882458135528892006302;
        xtypes[7] = 1763024496842125922962385602205943197321056192857025632119827849480763839;
        xtypes[8] = 1675710049990272911441037604203877133533668492266557815129548233759518128;
        xtypes[9] = 1551233773361696239304903403895808160934653895059654415324688197841582071;
        xtypes[10] = 1625636607414481457642678052301313971780849938445042085056482070100953889;
        xtypes[11] = 1722512524339940313921309205786012536301170577779571168786046988462044871;
        xtypes[12] = 1766836768210894802335784057954424698551920770841938248704295415987566568;
        xtypes[13] = 442714978489977024950392528240390319461857033012516340293718171539472383;
        xtypes[14] = 1198354148602851882454841352599916552948941833809782804420285718256091136;
        xtypes[15] = 1765862763184508503075712492699913383911672107677599518220277459367559248;

    
        for(i=0;i<=4;i++) {
            pos = (params[0]-1) * 5 + i;
            colors[i] = toHashCode(xtypes[pos/10] / (16777216 ** (pos%10)) % 16777216);
        }


        output = string(abi.encodePacked('<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" width="1000px" height="1000px" viewBox="0 0 1000 1000"><linearGradient id="g" gradientUnits="userSpaceOnUse" x2="0" y2="1000"><stop offset="0.1" style="stop-color:#',colors[2],'"/><stop offset="0.5" style="stop-color:#',colors[1],'"/><stop offset="1" style="stop-color:#',colors[2],'"/></linearGradient><rect x="0" y="0" fill="url(#g)" width="1000" height="1000"/>',
        '<circle opacity="0.09" fill="#FFFFFF" cx="726" cy="380" r="151"/><radialGradient id="s" cx="44" cy="723" r="99" gradientTransform="matrix(1 0 0 -1 662 1118)" gradientUnits="userSpaceOnUse"><stop  offset="0.6" style="stop-color:#FFFFFF"/><stop  offset="1" style="stop-color:#',colors[1],'"/></radialGradient><circle opacity="0.8" fill="url(#s)" cx="706" cy="395" r="99"/>',
        getPolygon(92 +params[6], colors[2], '0.08')));
        
        output = string(abi.encodePacked(output,'<g><path opacity="0.11" fill="#FFFFFF" d="M668,533c0,0,313,0,303-28s-397-48,15-46c301,2,196,46,460,43c258-3,484-60,485-17s-290,24-290,50s288,9,478,12c189,3,341-83,535-26c194,56,322,32,517,5c194-27,140,0,427,0s1180,0,1180,0v-31H3607c0,0-137-11-283-10c-146,0-162,33-394-7c-232-41-312-43-420-24c-107,18-255,68-395,74s-397,18-398,7c-1-10,447-26,446-65c-1-38-176-42-391-25c-215,16-395-14-568-19c-172-4-626-8-627,25s323,25,322,50c0,9-593,3-593,3H-36v27h478C442,531,553,533,668,533"/><path opacity="0.11" fill="#FFFFFF" d="M668,514c0,0,322,12,312-15c-10-28-804-56,8-62c301-2,239,37,503,34c258-3,565-42,569,1c2,29-381,32-417,61c-23,18,273,12,470,7c189-5,513-94,714-52c212,44,184,46,379,18c194-27,188,1,475,1s1096,0,1096,0v-20c0,0-1102,0-1175,0s-137-13-284-13c-146,0-161,25-393-16s-315-31-422-12c-107,18-250,68-389,74s-432,24-432,13c0-13,458-25,473-71c12-37-172-54-387-38s-395-14-568-19c-172-4-620,7-621,41s319,32,321,51c0,9-598-12-598-12H-36v27h478C442,513,553,514,668,514z"/><animateMotion path="M 0 0 L -3750 40 Z" dur="150s" repeatCount="indefinite" /></g>',
        getPolygon(80 +params[5]*2-1, colors[2], '0.1'),
        getPolygon(80 +params[5]*2, colors[2], '0.4')));
        
        output = string(abi.encodePacked(output, getPolygon(0, colors[2], '1'),
        getPolygon(1, colors[1], '1'),
        getPolygon(2, colors[0], '0.2'),
        getPolygon(3, colors[2], '0.3'),
        getPolygon(4, colors[2], '0.3'),
        getPolygon(5, colors[0], '0.2')));
        
        output = string(abi.encodePacked(output, getPolygon(6, colors[4], '1'),
        getPolygon(7, colors[3], '1'),
        getPolygon(8, 'FFFFFF', '0.9'),
        getPolygon(9, 'FFFFFF', '0.9'),
        '<circle fill="#',colors[2],'" cx="566" cy="406" r="7.3"/><circle opacity="0.66" fill="#FFFFFF" cx="570" cy="404" r="3"/><circle fill="#',colors[2],'" cx="414" cy="407" r="7.3"/><circle opacity="0.66" fill="#FFFFFF" cx="418" cy="404" r="3"/><g>'));
        
        output = string(abi.encodePacked(output, getPolygon(77, colors[3],'1'),
        getPolygon(78, colors[3], '1'),
        getPolygon(79, colors[4], '1'),
        getPolygon(80, colors[4], '1'),
        '<animate attributeName="opacity" values="0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0" dur="4s" repeatCount="indefinite" begin="0s"/></g>'));
        
        
        if (params[7] == 1) {
            output = string(abi.encodePacked(output, getPolygon(38, colors[0],'1'),
            getPolygon(39, colors[3], '1'),
            getPolygon(40, colors[3], '1')));
        }
        
        output = string(abi.encodePacked(output, 
        getPolygon(9 +params[1]*2-1, colors[3], '1'),
        getPolygon(9 +params[1]*2, colors[2], '0.66'),
        
        getPolygon(19 +params[2]*3-2, colors[2], '1'),
        getPolygon(19 +params[2]*3-1, colors[1], '0.6'),
        getPolygon(19 +params[2]*3, colors[1], '0.6')));
        
        output = string(abi.encodePacked(output, 
        getPolygon(40 +params[3]*4-3, colors[3], '0.92'),
        getPolygon(40 +params[3]*4-2, colors[0], '0.25'),
        getPolygon(40 +params[3]*4-1, colors[0], '0.25'),
        getPolygon(40 +params[3]*4, colors[2], '1')));
        
        output = string(abi.encodePacked(output, 
        getPolygon(60 +params[4]*4-3, colors[2], '1'),
        getPolygon(60 +params[4]*4-2, colors[1], '0.5'),
        getPolygon(60 +params[4]*4-1, colors[1], '0.5'),
        getPolygon(60 +params[4]*4, colors[1], '1'),
        '<g>',
        getPolygon(98, colors[2], '1'),
        '<animateMotion path="M0,0 C5,-5 12,-5 15,-12Z" dur="10s" repeatCount="indefinite"/></g></svg> '));

        
        string memory strparams;

        strparams = string(abi.encodePacked('[{ "trait_type": "Palette", "value": "',
        toString(params[0]),
        '" }, { "trait_type": "Beard", "value": "',
        toString(params[1]),
        '" }, { "trait_type": "Cap", "value": "',
        toString(params[2])));
        
        strparams = string(abi.encodePacked(strparams, 
        '" }, { "trait_type": "Glasses", "value": "',
        toString(params[3]),
        '" }, { "trait_type": "Tube", "value": "',
        toString(params[4]),
        '" }, { "trait_type": "Object", "value": "',
        toString(params[5])));
        
        if (params[7] == 1) {
            strparams = string(abi.encodePacked(strparams, 
             '" }, { "trait_type": "Band - aid", "value": "1'));
        }
        
        strparams = string(abi.encodePacked(strparams, 
        '" }, { "trait_type": "Far", "value": "',
        toString(params[6]),
        '" }]'));
    


        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Onchain Sandman", "description": "Onchain Sandman - beautiful avatar, completely generated OnChain","attributes":', strparams, ', "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }
    
    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }
    
        
    function burnAll() public onlyOwner {
        burned = true;
    }
    
    function directMint(address to, uint256 tokenId) public onlyOwner {
        require(!burned, "Burned!");
        _safeMint(to, tokenId);
    }
    
    function mintMany(uint32[] memory ids) public {
        require(!burned, "Burned!");
        require(saleIsActive, "Sale must be active to mint tokens");

        for (uint i = 0; i < ids.length; i++) {
            require(SandContract.ownerOf(ids[i]) == msg.sender, "Must own a Sand to mint token");
            _safeMint(msg.sender, ids[i]);
        }
    }
    
    function mintToken(uint tokenId) public {
        require(!burned, "Burned!");
        require(saleIsActive, "Sale must be active to mint tokens");
        require(SandContract.ownerOf(tokenId) == msg.sender, "Must own a Sand to mint token");

        _safeMint(msg.sender, tokenId);
    }

    
}




/// [MIT License]
/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <brecht@loopring.org>
library Base64 {
    bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /// @notice Encodes some bytes to the base64 representation
    function encode(bytes memory data) internal pure returns (string memory) {
        uint256 len = data.length;
        if (len == 0) return "";

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((len + 2) / 3);

        // Add some extra buffer at the end
        bytes memory result = new bytes(encodedLen + 32);

        bytes memory table = TABLE;

        assembly {
            let tablePtr := add(table, 1)
            let resultPtr := add(result, 32)

            for {
                let i := 0
            } lt(i, len) {

            } {
                i := add(i, 3)
                let input := and(mload(add(data, i)), 0xffffff)

                let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
                out := shl(224, out)

                mstore(resultPtr, out)

                resultPtr := add(resultPtr, 4)
            }

            switch mod(len, 3)
            case 1 {
                mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
            }
            case 2 {
                mstore(sub(resultPtr, 1), shl(248, 0x3d))
            }

            mstore(result, encodedLen)
        }

        return string(result);
    }
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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



// SPDX-License-Identifier: MIT

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() {
        _setOwner(_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 {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

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

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

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

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

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

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

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

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

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

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

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

        _transfer(from, to, tokenId);
    }

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

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

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        _beforeTokenTransfer(from, to, tokenId);

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

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

        emit Transfer(from, to, tokenId);
    }

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

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

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

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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



// SPDX-License-Identifier: MIT

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

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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);
            }
        }
    }
}

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, 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
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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 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);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

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



// SPDX-License-Identifier: MIT

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