ETH Price: $2,235.32 (+1.91%)
Gas: 4.85 Gwei

Transaction Decoder

Block:
11844731 at Feb-12-2021 11:01:54 PM +UTC
Transaction Fee:
0.511683018 ETH $1,143.78
Gas Used:
2,520,606 Gas / 203 Gwei

Emitted Events:

11 PackBuilderShop.BuilderInstanceCreated( new_contract_address=NiftyBuilderInstance, contractId=46 )

Account State Difference:

  Address   Before After State Difference Code
0x0CA9b12e...766C2FbfA
(Fake_Phishing326711)
1.809897283 Eth
Nonce: 1608
1.298214265 Eth
Nonce: 1609
0.511683018
(Nanopool)
2,571.275446167721136058 Eth2,571.787129185721136058 Eth0.511683018
0x5e156645...AE3a1Fa22
0xF8cCedDF...d2F60924E
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 609311911118283325099628539731968561558211949813969882151197271208888284724329998469066911424442206472176077023158435220320249875191545980426666590463569546704136355171361521594954255717103648113644645207606380166041279502337865500346507300589876406641593446944497737731494087709261433517811179095400861290217566913347031316090360671570435254868949860760590960500910345858694112389964367211916358631025228994414111716072707204148980937343339601864382521941397443979263687917779626350549831683287580166077127130072767218283745726114041883465136128460331675531183232273145088702517246756496797410714551990695411179705203629800875410651267986903867095040697069949690316365433555087493680665344537463417231987046866298629065788304950636189756149750004258957848973019588679156906922345145568005104263781397218963457219184326134973590036479152264933029804748642025631844827644175616748622028794462477913011460689542064519021690829467710976922569458634712904343338588810353867241988547564002499640601456769332076430344487859021822782946634192348603120276813897541115928123844160747532628429097780633990967092037940951360631372541219582674532258762028197712453409571343092579750128359245704074338268186990847941579779091390941690978502025529958556741655201907843276616338041972577280462050094251471600470140291515917796908643213056817031993397876872525025177620910090737420294364819364118038529592510396029230251228607117314896202101697648439068153940745137100226880232160326600322813014483752871630286501787195193975721083217979434148955999385916307879115692378263499020633256229324260239405496095970192812167030361386354717104116188785152367424790840339813392171823247997874813645865436375180846837733471853619310620374948385634169711200120028305943944583290875104476349380015032103268589021459417580942812359105177567517762904769548625075730771347147777088520504865764869038192534798147282835531749077175735127982826025261929774492978532566631662575166939872235373685759690425312822216144684941895003695532821503722081180461808159425610455649993994668124213421788717317756791683993051782432742858753870760467915152246067801965966017381265267327245715682595829701231292752390971890031959378691852810051134488177675939536827716205895932658805934683451061476527812683187132943592320636872451362856392152560094675178025369544171610126410240095126071149341798010810790608312951645714852774073576506035028666202953360273916056581483880394739423938528261368782655945716605304195091907268412077722634490060953739932987658295322696501737325970274168002668566312335853019824668531787911979737632522524643342116347906902089233000196029117117340218201355820074643063911945038182794079805092946048947721592951271945641318457737896850869544076192862399856031039957559588578630711626714295865833657448131556572349963786233119629077303387363203900142017813025204295735881307486911973443448776145851047828429440030260160137127499517405692877677845578187153690921975749324090463343997798785843876347725589300502741018417579588796223438228619857406253017007992140021061153587017015625889412511149539567009753576512664539266508962646078161660340681705711575923111012559147613733667482463541502561751537081046412404988677497398890599577877163533763970274518162231450961914607742832423129725883209536491893588338888542039272575227803696201961970263203551093887944198158795487846638533555745709143795834968602243447767557752494436946633600527100953695514148507962309944089767626490271134373830422479454176348951471135821121162760717937604288689388856039525773815944295073804368075330414146105951054429189432982884193862189268086442544498265702856853503582148177130002798063420627920009196082212721150384131090132006461323203047229510859475987928908753749338544781251811894909155627789813193973323967994364995259439721538181658110531986800453214849934620965926022155680279788448246152821898002381045045711777957059371246843628761759483974731334997138554298881463258586050376837555819675110072921397193633358799662244144145695553755139794416489347622951028409229467468393793991850204479653465793451009591242259740766772134267498184038730623252716283096302245018520323922278963112954606084096365907611256273013101670602509679377416634034446750044234400855341762577214996720149358352054259677558364091825525615358474591223803855288744757444675157828289736280440410271614487387245036342939857979074399495221439138971785987841113215136013117099274074731878075245726913578349353964434081333271524282940901357330059198410829639990831791423174497338090398472039267924904185740468626202409369535251191891496844810990382945050259146562767117986484807553281007061409535868366016412818925584615818227640691848329875829488123017695260439667823276010343113355994190158804270661058614531074041127650568149312612337075272364014420596332835466578811226261362289436394573008818403299898755523044734770139521451809228314884473159880692484801506008705905850012364822549939039216384469671348619327909697336363403228718424948422862777121908193665469368419347277745350677886456034046359629042701163600121401232748808362838812179774787895596086442248742755597524155981947130381670696617112585131601328947990186323005316070531871342863286431021402262403498724595870612517940446752390029421981338489183009596663157370476737304862624393684478837937019606323341417643295354376110465300133294730328081446148205330400686139920092455177168931198987298559092660199400446242022012659237351910009838444911894528290258204637284578655850748249024974892737335707947793205258126561256239530077030584449932340868716439854234433238379882135110629403572760117839844006909979906762257488103816721387135403463347877981931808380707468790562962597776105594511688132773259811554769994661079258196936176519425503393573990581130326983923403998819341631009117968940355209794259661140577119405274214682569203629102841135886177276202006703601333279228811657591694911625792168316234183669041183910333298662181250706001212972808314480876296199784887007560064670741045519992970545293964333017355080845292061750004880734327149761222854985863146062088939632111360799921531860459768808784282926567769001810010547184953959074026020101373185380397632173792255043505913354217694197849557093644591763829210387140242549328114013012682478012892842323926710509969740863910952194172617346475988669321474684841120590433828728296951874564253417830576211093296797259961999081442909517448221307418951954404950780090041228197463826980282631792703007675899271037788552512943252674873068956318391131730329005578410279061100030839780912340084888035226707448308425755590846392920795011702287424560005176501419136331052590846468537728133868709239341661858547214161344668930909076361362439829155268921408318071608499772384029046018334915023895883641201474499417154509195673171834194480788772277464183956943741698202317433234519259070850561995961691495644270665376109686505437636715071992976515674342406181653596439383282674738359621521642554064292648804336942525348717543853285309557917576795361112114457282548813869491863288541724704755879437041689787511870801329042006254644809865583181889462211955472763592405386838200499966451378887341417504979049900025120552089335425771451679476851775617584736787399524062804202123282979684018009293064043881251783963516871474912086217896915472704482895242313617462359917774060522201698026028061188241771669774045913123193379600187762274884198860229231341391870853312207931495433116278247777344360498882915393539600409754393652098390813495999773132146260294929877787306221256065773605247089711572110274358452108995210267318219687852135052624588728307779499938797545979483846736509456081407738108675146827396241667440813592275064271721660940947959999314368575394699010822727596769772689112791466252560002390276080749148695044608519617060852086586479302705146914309000610164261332383730770850144669689080357273652141697397635281048388226574033486673289882282973932802201979494454457372452404571746055882046061005648245378662202283072871503233425453139982874181355039422429075810638373866356570940784142447734763251809030379884544653903746971804535247811428233032136467224726187872540791235357174668325797233111936860412363612993070701534053044610453168980944195467090884118684279020568766660527815206257920984114549129234103077389847385710185450180115368180944508143237566674653479069780192055462821110020442140687871282124610082796624786969838013447166747674396531609535248755521837004965525941515667839876407560524389084591186303469520293941606265481658368674550175462919339669682757066882061352496426655427336001408422261446171223344765631542489602039262400583244756179054083724054035171255526758402496263725030578675935648955132607786644441467866578041292524775419015329455678559944158975500610602547300626052087397494487216294530502596133230741885270562664102066851306781086712738480991648233095955799295832654410282401843656608914821541373213712734997394415978726357411801594926095467334993054926219292402724714423994354487178470338801231121930226014492852964056690078495341396333878271581204604878576172374551250903285734099995754338346558495344674634205144197762911101828407577600588482526672250550701606365386933130771398004977688347500040106695686275441626035799135202564125976600099325016879804713966640405443408327012692986826138892633737761574280399472116525814218578692521828205455727091405191193457218915381705342006150526726929131583931179825675928003441458405078217177062192556260561304410218438441863632221316308700455702944997352646180000422436369396680576401245882811447814607353678153273730161159117171627579635887831439372138418459587683801707547419361788568803109978700128024095388843925825351538105126358141931421502565627883747079326823377921402656860530114655801983569134603128173396842412083041618673676153812501630456468986259891558717394157916723695263102290689023424255079620882237452983262727467737183798727959369456040446981339552444421269270126477514372465808704641721128159212039725663437742797401739730278519791987317838427740248698046706297488183122656635899838728032971136443818174097682536302808987358891902969036611944327987810717813172611878078751174615860264791750306290362657689560441128347550262124201891892432024857740176032850588814505144015800198818187003716237713820834877461174895236890800381043275035326579054832267166566973700446967645754431981150444276871264097242971177287972973104990497158791547264898420492296632344776223109770051416581974313050650869747939367020984846787389061461792722597488233436958117758355233470718567073671354469933854392917324293154576586583327719873243219919734414680333995565646982276156542477066889537533490675951634739658084050962200319213278783659425620653536165749779838298187080047560083808972365269259818188218909162829854426294115303447092840202477771214359073032519272627843930320421374518549627861086579454160957872886221339775036274838046346236851340208412334015008769944234162734345900382398658878684504411253552401191100513393411969165566319250938995223458199609471385505714605705266183635531593945534017263588855772078422604399630107965581365481860920368643827407796421937130628456145498611605580208751132272832555724517085848882600412997312919510232546377027298162730683530335634572903243594650654619340526560937549130652149910215941135752829660202922203394197947892354325744525088714353680537756908824046117183884182484849002247820700446686979231092221433805261724285851219451920511549353524107374485646870175021398639026757022983410885188852494776068558888770180302985580825153187867344068967954429901399706207500970444673625914266261240119747822890933496324533503186079735138635228676440610339336175874182743200406940600884207740751031994804523432769308246988860105050032521519259426700137270752374148239351327538045491539571624067593561361634663600623024974472962571257753674490230873412316290692823309515232225494269022783775884076770267026411548743918577308527830580594328924894628239227060017714371610777783583854530903150823492805117937055971178566859365712217999483699924228888364743639610108805823650268206485691313073347233259029611581729159710523569873634566644855673497004968283014818453207524891800498168264372651997245142860561722556211398290855069782122392747781065725857159169821666760782142970470446513188373669414971419484433950593728033422343973226233172797278494153579250153628295971151176871558810735037798326378579562828706387527918415322999334899803353006231970376414234353134194504245423281510719270522444129386452054969714830808820080625923454394316236883396478820161194981898260788717736576943957064959488182642402221804375601095137178448041073653210849667246747258874661572949752132338400577525696055982537524645304465037390079824573669674273157641305540811810566963433619548073172887541195082113991416344106767229458301104504524001951945640966294098545766661059906016738751876856212569639884189029739189762298092784866040184460877331541123243943127512053990843681769919643717530253514041141470944553217221203906762211380430397157926350302426691931490633209116971292752697451269787793915527669690841451173367208575712293762094175637411193646015594561205685765286323444302358173440510761048953056593387312203367652694767857743990449120801313500623820351068419262765051147339112617627057753786994000289386349392632555105747574891600697047954486669514065406989645533673296134154397398445167037230685142691663011582926917801602880763639455291451880747997407760477120188577188993969400357403528824554019967820131750495129623040878894419066551314765883515693265793858277287900019239930152732726909910340354526788803015977493461504410256845826517435321522348079268857116679656787530068197805772983076675583680145166617368361489564995660651255629391482787423935537132325699932219138979406188595242470508620556046745381064368337071889955557024334689836459851101690440499671359035163473375037930213623528982144608541503645475623268840654756200192411976945001022878800774602257516872292877221023315582907037777197814201942694576892734599033956017320366711290752501558954221198576352351328718919685314147448698403960845110488294756787199183159537708793098195996284563943955130851095187329472295474916476371619220772630825285288344364421282779100899294167737693521601991323766116286653639302430282733118039127140743278224923304758658887824958559006276377993403780270485198946566864094421077105585536404016775688587695650607417719405720879697880120024432740426745536940223087469614588177242306678001844124685590592354472728629903321553872679826347720529805049864580424047386370840243938951133098574532292063880819663563837376601844813995340819529458235017007886477382025209828202700410874824716577905785625421739959156425471331250934027099365718945676816346544613812677933175099545504718128285372373945461186511804636401772325967141154157408470564698410273235694235736121141700486756687472755419731249790910492639699645514290839258839499579509826143615601521839707016707614728408979642222625191524152805800401027281910264655468646792562240619283574391138872480541679080328732610964304699960004997322370895165844157954789801377329633365112508239602235817369642877967523972749616299215463512794510945285563905929756311785388905028745949315197971635826242689929599266966641322949384462233186019775204516487989622662873079133419835145233249019223726588762818009908325637072332741507199109142546439276566787039033815217744919493617725438455788160177761746175297459471556455034770060644373285931053768536279102430838357140010838722560259899980765920308067433879301447499747219141258714628235098242757520770614642249265439014417286903436770706136880915308799149194349619284224131056661734307634051328480987932693918431949118570213394409190737031020493378084221772341391118896638146935313014563305560667496383017433320014600194010372484657162257069101898847184860174385285691709768754943035122949947458165063649822206014019319348444481910062186951666744984205516886730281286087294817011964535281066404768948236131022953780585632959690394854103355844762656959396963937905319304680003098363128046205802101500625962432182429929752478100355567731918302446506191557338827436613465767609059411270572866760578914479100367747627776230780353229512507697911237584433836833323027141962774663522250223108338606973228936744729413084164346689584288477076220056098115582148559851818884028200677019031282028611005934409570531968601699873482448042503592984076993784232307389189926079284081010504340476023852770317596536155949096961384378251261974214834213766043000719310949127299471472273765269286906644572157031736174320181240392360836472665973618320853245274024825012198746135542853360815173010195481401244122017601112514963817869571784109877645895873956058037162496250829533455526651197214171740736024976032697698864533434363843277414454220683665342154244407282041710762357588580590966210932597941790484830783943860700477806220470401319770225247000268772947999115140207498581533337691953863204466750117265964516577811589106040385843434578443799170486374977693652452741424811306507185775551779004286016735424864754531354294607172835495647236901852281535119686898520560134208075853315519694119559335536088364146981094889166984037633629608278639063060081951083987181321410800841087517499508303228504424064290645357804747562238817099697635792659715746508287504785669811338807859348546822030983269703477125633199343240542619069460267493501180224013408006003405035202191407041841220409784653072700500051056450894528830272229140772932597139525926768636332591778837732462778379107934416401990276219757087677336592117792467461317110604228547275864832819992129471871750850257872591762630279938714506741677757838248321472677601432819085769036029365334205719042772595464927892729891181201260915735945700569325675601238445429872743720094593109011518165565131437755006391536242664681978609484952224586653032370740468587811948856703828655285429905642909311740717952154488315537994747978795888697540276882890791458280098448051955526291227541585064938973598739183631230729590286516370293306942681990883101071262354231248102151953622889095689439161705329212243159412534396849443820557014251785296666564571961470428941915027599402830254234515122840941864492124647125047139648666407826171744919684186874698476023798289595562176882119180269758744047819336201578498291606428589147454851155105178123417431224856393882011600101709072352155146534746498702805106531727302485539242811349857839617652511900371169676564099938412522082578120820806061258542089438632901838149465056354371517493827297510906675580245994396206988001127608013958779155056726202077941721696114791258605759443916582934053817483210872326710682154825253470076636167195651239739334392931592679792684956918199612645036516433270725391057199014285100882200183697848513590655266054168847535842609994515147848135491443135956055306516575454026841399589053934080811874736535255928279952391475304225716544806325549095517018427325401759194664787803794225820536239320264743724575828334278340094004482973166764035856361097911472962971636749083099340464631933959532704669446921114071008102074188972960665696902712620886186092477629688787487517159406561706604643757013692563318317992754378055389699629557791185702472374179262325142578541302206526228021372169722053408722041335456493667447178413239873522965499926627155179712785945198267098217914497043383279147822528718368919034790167968853222252386625014253416471058750735761378155745334505061509677349439924732269638500878106983402269572749696062804780028117031364869293270870860808550336448544671756420541680658659963784761119755732262400614015381818920011603644729252646770150999116247340294627527032531167445485618004021914393908797711210766501535150510619116015928749563875059859371460665295289497408989265264064779078620691979100347644478312377969880989638008478523545339805173975740820811963026920617317390290786077753117014152459385320128579004214270667090453249697359546468097481003464594437447017173697443552189580427795832206306695097360665163954791871342814117829435005737271822566293023606502558103466540174786936199549560891260392162130806208229273600810196821349856127087317698860969657794984833053164085510726448624268017053838411494945289202673078327354945794752201472316630690308105868983937020372195807838029289415441431936784327296977374151858836688977761854253446519298519057361124697610964615045622040370124463960434394025602815254181821103820853074428469845476058579947726304180040087723654460976897967603046040936476921684205173063272431587129414093745474609416470936865040377867255253685127959776511405093997198346186661267628370636172613949430951046681205096331310701474742479908337163638704749467188427847464821524881905733481294920020010505850721272156551618315525147579856131347725886599980201643952011647757103845282572739466629974435002561486964238361274921707728297394147693293950253876116173800712118414283766917829680743743560982251507178675452082477387453687285560955309087824884643392850060368523236391607785385971351347975187124893388877080299938809237519855887571686170704481846305246210331262375318722615274346948456961050156599239476113638517728946936786458575309931278258198322148346601866042207658939405999051773078014918881169161803498253948851847849531178093699271755339458356777704970055266482563242932380707168255082891572701219132116075773160478401163434071596119441956486413048208474217761099640765646253936302390388548643369782699608494495803732286850833948477701512455432033214952771725552746587927060026481634983107632635670186326252793447813591375555551349389879655893963123431982335646298516000657561634775781567629418894736561137162057140733773847399931203834229177532362966282556244421760809213090823117872085257477871594672809012956323402071948953999280447181359392765047504803626834897842869066064532524762507674925964564343798016586751159326660199619675854187662753532940645681353251285793777161583973815423526825323058580027295375687139856204432238826124792497426426086545073112273282442929116137256025157263146966172619760763671049219559372784451550589852078181198417672803104833649093372070813947723445901476524907359250971726849050551267378656242753963635334438058870684597367613145676487934032824048865045749170235740750364074059479509143944703255911132163051664329694659781293852282898454912010182700076794492188044568843342141583786597958618680835201607518964188035328281056235887626418197572243626978105181916201406245189122649363794617957720459728783999131182012071077041909090963485182540271460063203085966058715540377778723109864115194380631020509417346919858746984966301245555890063995747701808315254703423178850562830837613377168178379116765847187673450835265282987808485225666485591355212265647792880453571325153644261250907147970094277335246409699340244703479908003175420729073493107536154646881876876906624585693883627047967202272955997665673498937675858246147398444067433701812542059322263848226179711663073577298475827995865428876634064829520845938773512867427773126353369129538818702205736291785872746384807356972446664189374500045582258938200371005260992781711406908719748354568086645095843334557691284712733634796106745672287725012823528533221118073391523652200475967199093166603756529838933806580273729129911442663413804991905833

Execution Trace

PackBuilderShop.createNewBuilderInstance( _name=Kranky: Metal As Fuck Series by Superplastic x SketOne, _symbol=KRANKYMETALASFUCKSERIESBYSUPERPLASTICXSKETONE, num_nifties=6, nifty_quantities=[50, 50, 50, 50, 50, 50], token_base_uri=https://api.niftygateway.com/superplasticpacks/, creator_name=Ryan H ) => ( tokenAddress=0xF8cCedDFCd5329de91Be6a55edf99b7d2F60924E )
  • NiftyRegistry.isValidNiftySender( sending_key=0x0CA9b12e9cc22D2b9A1e20F6047A0e9766C2FbfA ) => ( True )
  • NiftyBuilderInstance.60806040( )
    createNewBuilderInstance[PackBuilderShop (ln:26)]
    File 1 of 3: PackBuilderShop
    pragma solidity ^0.5.0;
    
    contract PackBuilderShop {
       address[] builderInstances;
       uint contractId = 0;
    
       //nifty registry is hard coded
       address niftyRegistryContract = 0x6e53130dDfF21E3BC963Ee902005223b9A202106;
    
       modifier onlyValidSender() {
           NiftyRegistry nftg_registry = NiftyRegistry(niftyRegistryContract);
           bool is_valid = nftg_registry.isValidNiftySender(msg.sender);
           require(is_valid==true);
           _;
       }
    
       mapping (address => bool) public BuilderShops;
    
       function isValidBuilderShop(address builder_shop) public view returns (bool isValid) {
           //public function, allowing anyone to check if a contract address is a valid nifty gateway contract
           return(BuilderShops[builder_shop]);
       }
    
       event BuilderInstanceCreated(address new_contract_address, uint contractId);
    
       function createNewBuilderInstance(
           string memory _name,
           string memory _symbol,
           uint num_nifties,
           uint[] memory nifty_quantities,
           string memory token_base_uri,
           string memory creator_name) public onlyValidSender returns (NiftyBuilderInstance tokenAddress) {
    
           contractId = contractId + 1;
    
           NiftyBuilderInstance new_contract = new NiftyBuilderInstance(
               _name,
               _symbol,
               contractId,
               num_nifties,
               nifty_quantities,
               token_base_uri,
               creator_name
           );
    
           address externalId = address(new_contract);
    
           BuilderShops[externalId] = true;
    
           emit BuilderInstanceCreated(externalId, contractId);
    
           return (new_contract);
        }
    }
    
    /*
    * @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 GSN 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.
    */
    contract Context {
       // Empty internal constructor, to prevent people from mistakenly deploying
       // an instance of this contract, which should be used via inheritance.
       constructor () internal { }
       // solhint-disable-previous-line no-empty-blocks
    
       function _msgSender() internal view returns (address payable) {
           return msg.sender;
       }
    
       function _msgData() internal view returns (bytes memory) {
           this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
           return msg.data;
       }
    }
    
    /**
    * @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);
    }
    
    /**
    * @dev Implementation of the {IERC165} interface.
    *
    * Contracts may inherit from this and call {_registerInterface} to declare
    * their support of an interface.
    */
    contract ERC165 is IERC165 {
       /*
        * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
        */
       bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    
       /**
        * @dev Mapping of interface ids to whether or not it's supported.
        */
       mapping(bytes4 => bool) private _supportedInterfaces;
    
       constructor () internal {
           // Derived contracts need only register support for their own interfaces,
           // we register support for ERC165 itself here
           _registerInterface(_INTERFACE_ID_ERC165);
       }
    
       /**
        * @dev See {IERC165-supportsInterface}.
        *
        * Time complexity O(1), guaranteed to always use less than 30 000 gas.
        */
       function supportsInterface(bytes4 interfaceId) external view returns (bool) {
           return _supportedInterfaces[interfaceId];
       }
    
       /**
        * @dev Registers the contract as an implementer of the interface defined by
        * `interfaceId`. Support of the actual ERC165 interface is automatic and
        * registering its interface id is not required.
        *
        * See {IERC165-supportsInterface}.
        *
        * Requirements:
        *
        * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
        */
       function _registerInterface(bytes4 interfaceId) internal {
           require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
           _supportedInterfaces[interfaceId] = true;
       }
    }
    
    /**
    * @dev Required interface of an ERC721 compliant contract.
    */
    contract IERC721 is IERC165 {
       event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
       event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
       event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
       /**
        * @dev Returns the number of NFTs in `owner`'s account.
        */
       function balanceOf(address owner) public view returns (uint256 balance);
    
       /**
        * @dev Returns the owner of the NFT specified by `tokenId`.
        */
       function ownerOf(uint256 tokenId) public view returns (address owner);
    
       /**
        * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
        * another (`to`).
        *
        *
        *
        * Requirements:
        * - `from`, `to` cannot be zero.
        * - `tokenId` must be owned by `from`.
        * - If the caller is not `from`, it must be have been allowed to move this
        * NFT by either {approve} or {setApprovalForAll}.
        */
       function safeTransferFrom(address from, address to, uint256 tokenId) public;
       /**
        * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
        * another (`to`).
        *
        * Requirements:
        * - If the caller is not `from`, it must be approved to move this NFT by
        * either {approve} or {setApprovalForAll}.
        */
       function transferFrom(address from, address to, uint256 tokenId) public;
       function approve(address to, uint256 tokenId) public;
       function getApproved(uint256 tokenId) public view returns (address operator);
    
       function setApprovalForAll(address operator, bool _approved) public;
       function isApprovedForAll(address owner, address operator) public view returns (bool);
    
    
       function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
    }
    
    /**
    * @title ERC721 token receiver interface
    * @dev Interface for any contract that wants to support safeTransfers
    * from ERC721 asset contracts.
    */
    contract IERC721Receiver {
       /**
        * @notice Handle the receipt of an NFT
        * @dev The ERC721 smart contract calls this function on the recipient
        * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
        * otherwise the caller will revert the transaction. The selector to be
        * returned can be obtained as `this.onERC721Received.selector`. This
        * function MAY throw to revert and reject the transfer.
        * Note: the ERC721 contract address is always the message sender.
        * @param operator The address which called `safeTransferFrom` function
        * @param from The address which previously owned the token
        * @param tokenId The NFT identifier which is being transferred
        * @param data Additional data with no specified format
        * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
        */
       function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
       public returns (bytes4);
    }
    
    /**
    * @title ERC721 Non-Fungible Token Standard basic implementation
    * @dev see https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721 is Context, ERC165, IERC721 {
       using SafeMath for uint256;
       using Address for address;
       using Counters for Counters.Counter;
    
       // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
       // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
       bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
    
       // Mapping from token ID to owner
       mapping (uint256 => address) private _tokenOwner;
    
       // Mapping from token ID to approved address
       mapping (uint256 => address) private _tokenApprovals;
    
       // Mapping from owner to number of owned token
       mapping (address => Counters.Counter) private _ownedTokensCount;
    
       // Mapping from owner to operator approvals
       mapping (address => mapping (address => bool)) private _operatorApprovals;
    
       /*
        *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
        *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
        *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
        *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
        *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
        *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
        *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
        *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
        *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
        *
        *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
        *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
        */
       bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    
       constructor () public {
           // register the supported interfaces to conform to ERC721 via ERC165
           _registerInterface(_INTERFACE_ID_ERC721);
       }
    
       /**
        * @dev Gets the balance of the specified address.
        * @param owner address to query the balance of
        * @return uint256 representing the amount owned by the passed address
        */
       function balanceOf(address owner) public view returns (uint256) {
           require(owner != address(0), "ERC721: balance query for the zero address");
    
           return _ownedTokensCount[owner].current();
       }
    
       /**
        * @dev Gets the owner of the specified token ID.
        * @param tokenId uint256 ID of the token to query the owner of
        * @return address currently marked as the owner of the given token ID
        */
       function ownerOf(uint256 tokenId) public view returns (address) {
           address owner = _tokenOwner[tokenId];
           require(owner != address(0), "ERC721: owner query for nonexistent token");
    
           return owner;
       }
    
       /**
        * @dev Approves another address to transfer the given token ID
        * The zero address indicates there is no approved address.
        * There can only be one approved address per token at a given time.
        * Can only be called by the token owner or an approved operator.
        * @param to address to be approved for the given token ID
        * @param tokenId uint256 ID of the token to be approved
        */
       function approve(address to, uint256 tokenId) public {
           address owner = 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"
           );
    
           _tokenApprovals[tokenId] = to;
           emit Approval(owner, to, tokenId);
       }
    
       /**
        * @dev Gets the approved address for a token ID, or zero if no address set
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to query the approval of
        * @return address currently approved for the given token ID
        */
       function getApproved(uint256 tokenId) public view returns (address) {
           require(_exists(tokenId), "ERC721: approved query for nonexistent token");
    
           return _tokenApprovals[tokenId];
       }
    
       /**
        * @dev Sets or unsets the approval of a given operator
        * An operator is allowed to transfer all tokens of the sender on their behalf.
        * @param to operator address to set the approval
        * @param approved representing the status of the approval to be set
        */
       function setApprovalForAll(address to, bool approved) public {
           require(to != _msgSender(), "ERC721: approve to caller");
    
           _operatorApprovals[_msgSender()][to] = approved;
           emit ApprovalForAll(_msgSender(), to, approved);
       }
    
       /**
        * @dev Tells whether an operator is approved by a given owner.
        * @param owner owner address which you want to query the approval of
        * @param operator operator address which you want to query the approval of
        * @return bool whether the given operator is approved by the given owner
        */
       function isApprovedForAll(address owner, address operator) public view returns (bool) {
           return _operatorApprovals[owner][operator];
       }
    
       /**
        * @dev Transfers the ownership of a given token ID to another address.
        * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
        * Requires the msg.sender to be the owner, approved, or operator.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function transferFrom(address from, address to, uint256 tokenId) public {
           //solhint-disable-next-line max-line-length
           require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    
           _transferFrom(from, to, tokenId);
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the msg.sender to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function safeTransferFrom(address from, address to, uint256 tokenId) public {
           safeTransferFrom(from, to, tokenId, "");
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the _msgSender() to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        * @param _data bytes data to send along with a safe transfer check
        */
       function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
           require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
           _safeTransferFrom(from, to, tokenId, _data);
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the msg.sender to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        * @param _data bytes data to send along with a safe transfer check
        */
       function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal {
           _transferFrom(from, to, tokenId);
           require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
       }
    
       /**
        * @dev Returns whether the specified token exists.
        * @param tokenId uint256 ID of the token to query the existence of
        * @return bool whether the token exists
        */
       function _exists(uint256 tokenId) internal view returns (bool) {
           address owner = _tokenOwner[tokenId];
           return owner != address(0);
       }
    
       /**
        * @dev Returns whether the given spender can transfer a given token ID.
        * @param spender address of the spender to query
        * @param tokenId uint256 ID of the token to be transferred
        * @return bool whether the msg.sender is approved for the given token ID,
        * is an operator of the owner, or is the owner of the token
        */
       function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
           require(_exists(tokenId), "ERC721: operator query for nonexistent token");
           address owner = ownerOf(tokenId);
           return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
       }
    
       /**
        * @dev Internal function to safely mint a new token.
        * Reverts if the given token ID already exists.
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _safeMint(address to, uint256 tokenId) internal {
           _safeMint(to, tokenId, "");
       }
    
       /**
        * @dev Internal function to safely mint a new token.
        * Reverts if the given token ID already exists.
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        * @param _data bytes data to send along with a safe transfer check
        */
       function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
           _mint(to, tokenId);
           require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
       }
    
       /**
        * @dev Internal function to mint a new token.
        * Reverts if the given token ID already exists.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _mint(address to, uint256 tokenId) internal {
           require(to != address(0), "ERC721: mint to the zero address");
           require(!_exists(tokenId), "ERC721: token already minted");
    
           _tokenOwner[tokenId] = to;
           _ownedTokensCount[to].increment();
    
           emit Transfer(address(0), to, tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use {_burn} instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(address owner, uint256 tokenId) internal {
           require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
    
           _clearApproval(tokenId);
    
           _ownedTokensCount[owner].decrement();
           _tokenOwner[tokenId] = address(0);
    
           emit Transfer(owner, address(0), tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(uint256 tokenId) internal {
           _burn(ownerOf(tokenId), tokenId);
       }
    
       /**
        * @dev Internal function to transfer ownership of a given token ID to another address.
        * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _transferFrom(address from, address to, uint256 tokenId) internal {
           require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
           require(to != address(0), "ERC721: transfer to the zero address");
    
           _clearApproval(tokenId);
    
           _ownedTokensCount[from].decrement();
           _ownedTokensCount[to].increment();
    
           _tokenOwner[tokenId] = to;
    
           emit Transfer(from, 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.
        *
        * This function is deprecated.
        * @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)
           internal returns (bool)
       {
           if (!to.isContract()) {
               return true;
           }
    
           bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data);
           return (retval == _ERC721_RECEIVED);
       }
    
       /**
        * @dev Private function to clear current approval of a given token ID.
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _clearApproval(uint256 tokenId) private {
           if (_tokenApprovals[tokenId] != address(0)) {
               _tokenApprovals[tokenId] = address(0);
           }
       }
    }
    
    /**
    * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract IERC721Enumerable is IERC721 {
       function totalSupply() public view returns (uint256);
       function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId);
    
       function tokenByIndex(uint256 index) public view returns (uint256);
    }
    
    /**
    * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721Enumerable is Context, ERC165, ERC721, IERC721Enumerable {
       // Mapping from owner to list of owned token IDs
       mapping(address => 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;
    
       /*
        *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
        *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
        *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
        *
        *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
        */
       bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
    
       /**
        * @dev Constructor function.
        */
       constructor () public {
           // register the supported interface to conform to ERC721Enumerable via ERC165
           _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
       }
    
       /**
        * @dev Gets the token ID at a given index of the tokens list of the requested owner.
        * @param owner address owning the tokens list to be accessed
        * @param index uint256 representing the index to be accessed of the requested tokens list
        * @return uint256 token ID at the given index of the tokens list owned by the requested address
        */
       function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
           require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
           return _ownedTokens[owner][index];
       }
    
       /**
        * @dev Gets the total amount of tokens stored by the contract.
        * @return uint256 representing the total amount of tokens
        */
       function totalSupply() public view returns (uint256) {
           return _allTokens.length;
       }
    
       /**
        * @dev Gets the token ID at a given index of all the tokens in this contract
        * Reverts if the index is greater or equal to the total number of tokens.
        * @param index uint256 representing the index to be accessed of the tokens list
        * @return uint256 token ID at the given index of the tokens list
        */
       function tokenByIndex(uint256 index) public view returns (uint256) {
           require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
           return _allTokens[index];
       }
    
       /**
        * @dev Internal function to transfer ownership of a given token ID to another address.
        * As opposed to transferFrom, this imposes no restrictions on msg.sender.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _transferFrom(address from, address to, uint256 tokenId) internal {
           super._transferFrom(from, to, tokenId);
    
           _removeTokenFromOwnerEnumeration(from, tokenId);
    
           _addTokenToOwnerEnumeration(to, tokenId);
       }
    
       /**
        * @dev Internal function to mint a new token.
        * Reverts if the given token ID already exists.
        * @param to address the beneficiary that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _mint(address to, uint256 tokenId) internal {
           super._mint(to, tokenId);
    
           _addTokenToOwnerEnumeration(to, tokenId);
    
           _addTokenToAllTokensEnumeration(tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use {ERC721-_burn} instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(address owner, uint256 tokenId) internal {
           super._burn(owner, tokenId);
    
           _removeTokenFromOwnerEnumeration(owner, tokenId);
           // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
           _ownedTokensIndex[tokenId] = 0;
    
           _removeTokenFromAllTokensEnumeration(tokenId);
       }
    
       /**
        * @dev Gets the list of token IDs of the requested owner.
        * @param owner address owning the tokens
        * @return uint256[] List of token IDs owned by the requested address
        */
       function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
           return _ownedTokens[owner];
       }
    
       /**
        * @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 {
           _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
           _ownedTokens[to].push(tokenId);
       }
    
       /**
        * @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 = _ownedTokens[from].length.sub(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
           _ownedTokens[from].length--;
    
           // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
           // lastTokenId, or just over the end of the array if the token was the last one).
       }
    
       /**
        * @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.sub(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
           _allTokens.length--;
           _allTokensIndex[tokenId] = 0;
       }
    }
    
    /**
    * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract IERC721Metadata is IERC721 {
       function name() external view returns (string memory);
       function symbol() external view returns (string memory);
       function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    
    contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata {
       // Token name
       string private _name;
    
       // Token symbol
       string private _symbol;
    
       // Optional mapping for token URIs
       mapping(uint256 => string) private _tokenURIs;
      
       
       //Optional mapping for IPFS link to canonical image file
       mapping(uint256 => string) private _tokenIPFSHashes;
    
       /*
        *     bytes4(keccak256('name()')) == 0x06fdde03
        *     bytes4(keccak256('symbol()')) == 0x95d89b41
        *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
        *
        *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
        */
       bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
    
       /**
        * @dev Constructor function
        */
       constructor (string memory name, string memory symbol) public {
           _name = name;
           _symbol = symbol;
    
           // register the supported interfaces to conform to ERC721 via ERC165
           _registerInterface(_INTERFACE_ID_ERC721_METADATA);
       }
    
       /**
        * @dev Gets the token name.
        * @return string representing the token name
        */
       function name() external view returns (string memory) {
           return _name;
       }
    
       /**
        * @dev Gets the token symbol.
        * @return string representing the token symbol
        */
       function symbol() external view returns (string memory) {
           return _symbol;
       }
    
       /**
        * @dev Returns an URI for a given token ID.
        * Throws if the token ID does not exist. May return an empty string.
        * @param tokenId uint256 ID of the token to query
        */
       function tokenURI(uint256 tokenId) external view returns (string memory) {
           require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
           return _tokenURIs[tokenId];
       }
       
       
         /**
        * @dev Returns an URI for a given token ID.
        * Throws if the token ID does not exist. May return an empty string.
        * @param tokenId uint256 ID of the token to query
        */
       function tokenIPFSHash(uint256 tokenId) external view returns (string memory) {
           require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
           return _tokenIPFSHashes[tokenId];
       }
    
       /**
        * @dev Internal function to set the token URI for a given token.
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to set its URI
        * @param uri string URI to assign
        */
       function _setTokenURI(uint256 tokenId, string memory uri) internal {
           require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
           _tokenURIs[tokenId] = uri;
       }
       
          /**
        * @dev Internal function to set the token IPFS hash for a given token.
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to set its URI
        * @param ipfs_hash string IPFS link to assign
        */
       function _setTokenIPFSHash(uint256 tokenId, string memory ipfs_hash) internal {
           require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
           _tokenIPFSHashes[tokenId] = ipfs_hash;
       }
       
       
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use _burn(uint256) instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned by the msg.sender
        */
       function _burn(address owner, uint256 tokenId) internal {
           super._burn(owner, tokenId);
    
           // Clear metadata (if any)
           if (bytes(_tokenURIs[tokenId]).length != 0) {
               delete _tokenURIs[tokenId];
           }
       }
    }
    
    
    /**
    * @title Full ERC721 Token
    * @dev This implementation includes all the required and some optional functionality of the ERC721 standard
    * Moreover, it includes approve all functionality using operator terminology.
    *
    * See https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721Full is ERC721, ERC721Enumerable, ERC721Metadata {
       constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
           // solhint-disable-previous-line no-empty-blocks
       }
    }
    
    
    contract NiftyBuilderInstance is ERC721Full {
    
       //MODIFIERS
    
       modifier onlyValidSender() {
           NiftyRegistry nftg_registry = NiftyRegistry(niftyRegistryContract);
           bool is_valid = nftg_registry.isValidNiftySender(msg.sender);
           require(is_valid==true);
           _;
       }
    
       //CONSTANTS
    
       // how many nifties this contract is selling
       // used for metadat retrieval
       uint public numNiftiesCurrentlyInContract;
    
       //id of this contract for metadata server
       uint public contractId;
    
       //baseURI for metadata server
       string public baseURI;
    
    //   //name of creator
    //   string public creatorName;
    
       string public nameOfCreator;
    
       //nifty registry contract
       address public niftyRegistryContract = 0x6e53130dDfF21E3BC963Ee902005223b9A202106;
    
       //master builder - ONLY DOES STATIC CALLS
       address public masterBuilderContract = 0x6EFB06cF568253a53C7511BD3c31AB28BecB0192;
    
       using Counters for Counters.Counter;
    
       //MAPPINGS
    
       //mappings for token Ids
       mapping (uint => Counters.Counter) public _numNiftyMinted;
       mapping (uint => uint) public _numNiftyPermitted;
       mapping (uint => string) public _niftyIPFSHashes;
       mapping (uint => bool) public _IPFSHashHasBeenSet;
    
       //EVENTS
    
       //purchase + creation events
       event NiftyPurchased(address _buyer, uint256 _amount, uint _tokenId);
       event NiftyCreated(address new_owner, uint _niftyType, uint _tokenId);
    
       //CONSTRUCTOR FUNCTION
    
       constructor(
           string memory _name,
           string memory _symbol,
           uint contract_id,
           uint num_nifties,
           uint[] memory nifty_quantities,
           string memory base_uri,
           string memory name_of_creator) ERC721Full(_name, _symbol) public {
    
           //set local variables based on inputs
           contractId = contract_id;
           numNiftiesCurrentlyInContract = num_nifties;
           baseURI = base_uri;
           nameOfCreator = name_of_creator;
           //offset starts at 1 - there is no niftyType of 0
           for (uint i=0; i<(num_nifties); i++) {
               _numNiftyPermitted[i+1] = nifty_quantities[i];
           }
       }
       
       function setNiftyIPFSHash(uint niftyType, 
                                string memory ipfs_hash) onlyValidSender public {
            //can only be set once
            if (_IPFSHashHasBeenSet[niftyType] == true) {
                revert("Can only be set once");
            } else {
                _niftyIPFSHashes[niftyType] = ipfs_hash;
                _IPFSHashHasBeenSet[niftyType]  = true;
            }
        }
    
       function isNiftySoldOut(uint niftyType) public view returns (bool) {
           if (niftyType > numNiftiesCurrentlyInContract) {
               return true;
           }
           if (_numNiftyMinted[niftyType].current() > _numNiftyPermitted[niftyType]) {
               return (true);
           } else {
               return (false);
           }
       }
    
       function _giftNifty(address collector_address, 
                          uint niftyType) private {
           //master for static calls
           BuilderMaster bm = BuilderMaster(masterBuilderContract);
           _numNiftyMinted[niftyType].increment();
           //check if this nifty is sold out
           if (isNiftySoldOut(niftyType)==true) {
               revert("Nifty sold out!");
           }
           //mint a nifty
           uint specificTokenId = _numNiftyMinted[niftyType].current();
           uint tokenId = bm.encodeTokenId(contractId, niftyType, specificTokenId);
           string memory tokenIdStr = bm.uint2str(tokenId);
           string memory tokenURI = bm.strConcat(baseURI, tokenIdStr);
           string memory ipfsHash = _niftyIPFSHashes[niftyType];
           //mint token
           _mint(collector_address, tokenId);
           _setTokenURI(tokenId, tokenURI);
           _setTokenIPFSHash(tokenId, ipfsHash);
           //do events
           emit NiftyCreated(collector_address, niftyType, tokenId);
       }
       
       function createNifties(address collector_address, 
                          uint[] memory listOfNiftyTypes) onlyValidSender public {
            //loop through array and create nifties
            for (uint i=0; i < listOfNiftyTypes.length; i++) {
                _giftNifty(collector_address,listOfNiftyTypes[i]);
            }
                              
        }
    
    }
    
    contract NiftyRegistry {
       function isValidNiftySender(address sending_key) public view returns (bool);
       function isOwner(address owner_key) public view returns (bool);
    }
    
    contract BuilderMaster {
       function getContractId(uint tokenId) public view returns (uint);
       function getNiftyTypeId(uint tokenId) public view returns (uint);
       function getSpecificNiftyNum(uint tokenId) public view returns (uint);
       function encodeTokenId(uint contractId, uint niftyType, uint specificNiftyNum) public view returns (uint);
       function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) public view returns (string memory);
       function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) public view returns (string memory);
       function strConcat(string memory _a, string memory _b, string memory _c) public view returns (string memory);
       function strConcat(string memory _a, string memory _b) public view returns (string memory);
       function uint2str(uint _i) public view returns (string memory _uintAsString);
    }
    
    /**
    * Contracts and libraries below are from OpenZeppelin, except nifty builder instance
    **/
    
    
    /**
    * @dev Wrappers over Solidity's arithmetic operations with added overflow
    * checks.
    *
    * Arithmetic operations in Solidity wrap on overflow. This can easily result
    * in bugs, because programmers usually assume that an overflow raises an
    * error, which is the standard behavior in high level programming languages.
    * `SafeMath` restores this intuition by reverting the transaction when an
    * operation overflows.
    *
    * Using this library instead of the unchecked operations eliminates an entire
    * class of bugs, so it's recommended to use it always.
    */
    library SafeMath {
       /**
        * @dev Returns the addition of two unsigned integers, reverting on
        * overflow.
        *
        * Counterpart to Solidity's `+` operator.
        *
        * Requirements:
        * - Addition cannot overflow.
        */
       function add(uint256 a, uint256 b) internal pure returns (uint256) {
           uint256 c = a + b;
           require(c >= a, "SafeMath: addition overflow");
    
           return c;
       }
    
       /**
        * @dev Returns the subtraction of two unsigned integers, reverting on
        * overflow (when the result is negative).
        *
        * Counterpart to Solidity's `-` operator.
        *
        * Requirements:
        * - Subtraction cannot overflow.
        */
       function sub(uint256 a, uint256 b) internal pure returns (uint256) {
           return sub(a, b, "SafeMath: subtraction overflow");
       }
    
       /**
        * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
        * overflow (when the result is negative).
        *
        * Counterpart to Solidity's `-` operator.
        *
        * Requirements:
        * - Subtraction cannot overflow.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           require(b <= a, errorMessage);
           uint256 c = a - b;
    
           return c;
       }
    
       /**
        * @dev Returns the multiplication of two unsigned integers, reverting on
        * overflow.
        *
        * Counterpart to Solidity's `*` operator.
        *
        * Requirements:
        * - Multiplication cannot overflow.
        */
       function mul(uint256 a, uint256 b) internal pure returns (uint256) {
           // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
           // benefit is lost if 'b' is also tested.
           // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
           if (a == 0) {
               return 0;
           }
    
           uint256 c = a * b;
           require(c / a == b, "SafeMath: multiplication overflow");
    
           return c;
       }
    
       /**
        * @dev Returns the integer division of two unsigned integers. Reverts on
        * division by zero. The result is rounded towards zero.
        *
        * Counterpart to Solidity's `/` operator. Note: this function uses a
        * `revert` opcode (which leaves remaining gas untouched) while Solidity
        * uses an invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        */
       function div(uint256 a, uint256 b) internal pure returns (uint256) {
           return div(a, b, "SafeMath: division by zero");
       }
    
       /**
        * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
        * division by zero. The result is rounded towards zero.
        *
        * Counterpart to Solidity's `/` operator. Note: this function uses a
        * `revert` opcode (which leaves remaining gas untouched) while Solidity
        * uses an invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           // Solidity only automatically asserts when dividing by 0
           require(b > 0, errorMessage);
           uint256 c = a / b;
           // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
           return c;
       }
    
       /**
        * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
        * Reverts when dividing by zero.
        *
        * Counterpart to Solidity's `%` operator. This function uses a `revert`
        * opcode (which leaves remaining gas untouched) while Solidity uses an
        * invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        */
       function mod(uint256 a, uint256 b) internal pure returns (uint256) {
           return mod(a, b, "SafeMath: modulo by zero");
       }
    
       /**
        * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
        * Reverts with custom message when dividing by zero.
        *
        * Counterpart to Solidity's `%` operator. This function uses a `revert`
        * opcode (which leaves remaining gas untouched) while Solidity uses an
        * invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           require(b != 0, errorMessage);
           return a % b;
       }
    }
    
    /**
    * @dev Collection of functions related to the address type
    */
    library Address {
       /**
        * @dev Returns true if `account` is a contract.
        *
        * This test is non-exhaustive, and there may be false-negatives: during the
        * execution of a contract's constructor, its address will be reported as
        * not containing 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.
        */
       function isContract(address account) internal view returns (bool) {
           // This method relies in extcodesize, which returns 0 for contracts in
           // construction, since the code is only stored at the end of the
           // constructor execution.
    
           // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
           // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
           // for accounts without code, i.e. `keccak256('')`
           bytes32 codehash;
           bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
           // solhint-disable-next-line no-inline-assembly
           assembly { codehash := extcodehash(account) }
           return (codehash != 0x0 && codehash != accountHash);
       }
    
       /**
        * @dev Converts an `address` into `address payable`. Note that this is
        * simply a type cast: the actual underlying value is not changed.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function toPayable(address account) internal pure returns (address payable) {
           return address(uint160(account));
       }
    
       /**
        * @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");
    
           // solhint-disable-next-line avoid-call-value
           (bool success, ) = recipient.call.value(amount)("");
           require(success, "Address: unable to send value, recipient may have reverted");
       }
    }
    
    /**
    * @title Counters
    * @author Matt Condon (@shrugs)
    * @dev Provides counters that can only be incremented or decremented by one. 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;`
    * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
    * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
    * directly accessed.
    */
    library Counters {
       using SafeMath for uint256;
    
       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 {
           // The {SafeMath} overflow check can be skipped here, see the comment at the top
           counter._value += 1;
       }
    
       function decrement(Counter storage counter) internal {
           counter._value = counter._value.sub(1);
       }
    }

    File 2 of 3: NiftyRegistry
    /**
     *Submitted for verification at Etherscan.io on 2019-04-29
    */
    
    pragma solidity ^0.5.4;
    
    contract NiftyRegistry {
        
        
        event OwnerAddition(address indexed owner);
        event OwnerRemoval(address indexed owner);
        
        /**
         * Constants
         */ 
         
        uint constant public MAX_OWNER_COUNT = 50;
        
    
      /**
       * @dev Modifiers, mostly from the Gnosis Multisig
       */
        modifier onlyOwner() {
            require(isOwner[msg.sender] == true);
            _;
        }
      
       
       /** 
        * @dev A mapping of all sender keys
        */ 
        
       mapping(address => bool) validNiftyKeys;
       mapping (address => bool) public isOwner;
       
       /**
        * @dev Static view functions to retrieve information 
        */
         
        /**
        * @dev function to see if sending key is valid
        */
        
        function isValidNiftySender(address sending_key) public view returns (bool) {
          return(validNiftyKeys[sending_key]);
        }
        
          
          /**
           * @dev Functions to alter master contract information, such as HSM signing wallet keys, static contract
           * @dev All can only be changed by a multi sig transaciton so they have the onlyWallet modifier
           */ 
        
          /**
           * @dev Functions to add and remove nifty keys
           */
           
           function addNiftyKey(address new_sending_key) external onlyOwner {
               validNiftyKeys[new_sending_key] = true;
           }
           
           function removeNiftyKey(address sending_key) external onlyOwner {
               validNiftyKeys[sending_key] = false;
           }
      
      
      /**
       * Multisig transactions from https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
       * Used to call transactions that will modify the master contract
       * Plus maintain owners, etc
       */
       
       /// @dev Contract constructor sets initial owners and required number of confirmations.
        /// @param _owners List of initial owners.
        constructor(address[] memory _owners, address[] memory signing_keys)
            public
        {
            for (uint i=0; i<_owners.length; i++) {
                require(!isOwner[_owners[i]] && _owners[i] != address(0));
                isOwner[_owners[i]] = true;
            }
            for (uint i=0; i<signing_keys.length; i++) {
                require(signing_keys[i] != address(0));
                validNiftyKeys[signing_keys[i]] = true;
            }
        }
    
        /// @dev Allows to add a new owner. Transaction has to be sent by wallet.
        /// @param owner Address of new owner.
        function addOwner(address owner)
            public
            onlyOwner
        {
            isOwner[owner] = true;
            emit OwnerAddition(owner);
        }
    
        /// @dev Allows to remove an owner. Transaction has to be sent by wallet.
        /// @param owner Address of owner.
        function removeOwner(address owner)
            public
            onlyOwner
        {
            isOwner[owner] = false;
            emit OwnerRemoval(owner);
        }
    
     
    
    }

    File 3 of 3: NiftyBuilderInstance
    pragma solidity ^0.5.0;
    
    contract PackBuilderShop {
       address[] builderInstances;
       uint contractId = 0;
    
       //nifty registry is hard coded
       address niftyRegistryContract = 0x6e53130dDfF21E3BC963Ee902005223b9A202106;
    
       modifier onlyValidSender() {
           NiftyRegistry nftg_registry = NiftyRegistry(niftyRegistryContract);
           bool is_valid = nftg_registry.isValidNiftySender(msg.sender);
           require(is_valid==true);
           _;
       }
    
       mapping (address => bool) public BuilderShops;
    
       function isValidBuilderShop(address builder_shop) public view returns (bool isValid) {
           //public function, allowing anyone to check if a contract address is a valid nifty gateway contract
           return(BuilderShops[builder_shop]);
       }
    
       event BuilderInstanceCreated(address new_contract_address, uint contractId);
    
       function createNewBuilderInstance(
           string memory _name,
           string memory _symbol,
           uint num_nifties,
           uint[] memory nifty_quantities,
           string memory token_base_uri,
           string memory creator_name) public onlyValidSender returns (NiftyBuilderInstance tokenAddress) {
    
           contractId = contractId + 1;
    
           NiftyBuilderInstance new_contract = new NiftyBuilderInstance(
               _name,
               _symbol,
               contractId,
               num_nifties,
               nifty_quantities,
               token_base_uri,
               creator_name
           );
    
           address externalId = address(new_contract);
    
           BuilderShops[externalId] = true;
    
           emit BuilderInstanceCreated(externalId, contractId);
    
           return (new_contract);
        }
    }
    
    /*
    * @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 GSN 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.
    */
    contract Context {
       // Empty internal constructor, to prevent people from mistakenly deploying
       // an instance of this contract, which should be used via inheritance.
       constructor () internal { }
       // solhint-disable-previous-line no-empty-blocks
    
       function _msgSender() internal view returns (address payable) {
           return msg.sender;
       }
    
       function _msgData() internal view returns (bytes memory) {
           this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
           return msg.data;
       }
    }
    
    /**
    * @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);
    }
    
    /**
    * @dev Implementation of the {IERC165} interface.
    *
    * Contracts may inherit from this and call {_registerInterface} to declare
    * their support of an interface.
    */
    contract ERC165 is IERC165 {
       /*
        * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
        */
       bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    
       /**
        * @dev Mapping of interface ids to whether or not it's supported.
        */
       mapping(bytes4 => bool) private _supportedInterfaces;
    
       constructor () internal {
           // Derived contracts need only register support for their own interfaces,
           // we register support for ERC165 itself here
           _registerInterface(_INTERFACE_ID_ERC165);
       }
    
       /**
        * @dev See {IERC165-supportsInterface}.
        *
        * Time complexity O(1), guaranteed to always use less than 30 000 gas.
        */
       function supportsInterface(bytes4 interfaceId) external view returns (bool) {
           return _supportedInterfaces[interfaceId];
       }
    
       /**
        * @dev Registers the contract as an implementer of the interface defined by
        * `interfaceId`. Support of the actual ERC165 interface is automatic and
        * registering its interface id is not required.
        *
        * See {IERC165-supportsInterface}.
        *
        * Requirements:
        *
        * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
        */
       function _registerInterface(bytes4 interfaceId) internal {
           require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
           _supportedInterfaces[interfaceId] = true;
       }
    }
    
    /**
    * @dev Required interface of an ERC721 compliant contract.
    */
    contract IERC721 is IERC165 {
       event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
       event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
       event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
       /**
        * @dev Returns the number of NFTs in `owner`'s account.
        */
       function balanceOf(address owner) public view returns (uint256 balance);
    
       /**
        * @dev Returns the owner of the NFT specified by `tokenId`.
        */
       function ownerOf(uint256 tokenId) public view returns (address owner);
    
       /**
        * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
        * another (`to`).
        *
        *
        *
        * Requirements:
        * - `from`, `to` cannot be zero.
        * - `tokenId` must be owned by `from`.
        * - If the caller is not `from`, it must be have been allowed to move this
        * NFT by either {approve} or {setApprovalForAll}.
        */
       function safeTransferFrom(address from, address to, uint256 tokenId) public;
       /**
        * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
        * another (`to`).
        *
        * Requirements:
        * - If the caller is not `from`, it must be approved to move this NFT by
        * either {approve} or {setApprovalForAll}.
        */
       function transferFrom(address from, address to, uint256 tokenId) public;
       function approve(address to, uint256 tokenId) public;
       function getApproved(uint256 tokenId) public view returns (address operator);
    
       function setApprovalForAll(address operator, bool _approved) public;
       function isApprovedForAll(address owner, address operator) public view returns (bool);
    
    
       function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
    }
    
    /**
    * @title ERC721 token receiver interface
    * @dev Interface for any contract that wants to support safeTransfers
    * from ERC721 asset contracts.
    */
    contract IERC721Receiver {
       /**
        * @notice Handle the receipt of an NFT
        * @dev The ERC721 smart contract calls this function on the recipient
        * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
        * otherwise the caller will revert the transaction. The selector to be
        * returned can be obtained as `this.onERC721Received.selector`. This
        * function MAY throw to revert and reject the transfer.
        * Note: the ERC721 contract address is always the message sender.
        * @param operator The address which called `safeTransferFrom` function
        * @param from The address which previously owned the token
        * @param tokenId The NFT identifier which is being transferred
        * @param data Additional data with no specified format
        * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
        */
       function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
       public returns (bytes4);
    }
    
    /**
    * @title ERC721 Non-Fungible Token Standard basic implementation
    * @dev see https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721 is Context, ERC165, IERC721 {
       using SafeMath for uint256;
       using Address for address;
       using Counters for Counters.Counter;
    
       // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
       // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
       bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
    
       // Mapping from token ID to owner
       mapping (uint256 => address) private _tokenOwner;
    
       // Mapping from token ID to approved address
       mapping (uint256 => address) private _tokenApprovals;
    
       // Mapping from owner to number of owned token
       mapping (address => Counters.Counter) private _ownedTokensCount;
    
       // Mapping from owner to operator approvals
       mapping (address => mapping (address => bool)) private _operatorApprovals;
    
       /*
        *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
        *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
        *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
        *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
        *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
        *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
        *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
        *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
        *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
        *
        *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
        *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
        */
       bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    
       constructor () public {
           // register the supported interfaces to conform to ERC721 via ERC165
           _registerInterface(_INTERFACE_ID_ERC721);
       }
    
       /**
        * @dev Gets the balance of the specified address.
        * @param owner address to query the balance of
        * @return uint256 representing the amount owned by the passed address
        */
       function balanceOf(address owner) public view returns (uint256) {
           require(owner != address(0), "ERC721: balance query for the zero address");
    
           return _ownedTokensCount[owner].current();
       }
    
       /**
        * @dev Gets the owner of the specified token ID.
        * @param tokenId uint256 ID of the token to query the owner of
        * @return address currently marked as the owner of the given token ID
        */
       function ownerOf(uint256 tokenId) public view returns (address) {
           address owner = _tokenOwner[tokenId];
           require(owner != address(0), "ERC721: owner query for nonexistent token");
    
           return owner;
       }
    
       /**
        * @dev Approves another address to transfer the given token ID
        * The zero address indicates there is no approved address.
        * There can only be one approved address per token at a given time.
        * Can only be called by the token owner or an approved operator.
        * @param to address to be approved for the given token ID
        * @param tokenId uint256 ID of the token to be approved
        */
       function approve(address to, uint256 tokenId) public {
           address owner = 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"
           );
    
           _tokenApprovals[tokenId] = to;
           emit Approval(owner, to, tokenId);
       }
    
       /**
        * @dev Gets the approved address for a token ID, or zero if no address set
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to query the approval of
        * @return address currently approved for the given token ID
        */
       function getApproved(uint256 tokenId) public view returns (address) {
           require(_exists(tokenId), "ERC721: approved query for nonexistent token");
    
           return _tokenApprovals[tokenId];
       }
    
       /**
        * @dev Sets or unsets the approval of a given operator
        * An operator is allowed to transfer all tokens of the sender on their behalf.
        * @param to operator address to set the approval
        * @param approved representing the status of the approval to be set
        */
       function setApprovalForAll(address to, bool approved) public {
           require(to != _msgSender(), "ERC721: approve to caller");
    
           _operatorApprovals[_msgSender()][to] = approved;
           emit ApprovalForAll(_msgSender(), to, approved);
       }
    
       /**
        * @dev Tells whether an operator is approved by a given owner.
        * @param owner owner address which you want to query the approval of
        * @param operator operator address which you want to query the approval of
        * @return bool whether the given operator is approved by the given owner
        */
       function isApprovedForAll(address owner, address operator) public view returns (bool) {
           return _operatorApprovals[owner][operator];
       }
    
       /**
        * @dev Transfers the ownership of a given token ID to another address.
        * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
        * Requires the msg.sender to be the owner, approved, or operator.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function transferFrom(address from, address to, uint256 tokenId) public {
           //solhint-disable-next-line max-line-length
           require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    
           _transferFrom(from, to, tokenId);
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the msg.sender to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function safeTransferFrom(address from, address to, uint256 tokenId) public {
           safeTransferFrom(from, to, tokenId, "");
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the _msgSender() to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        * @param _data bytes data to send along with a safe transfer check
        */
       function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
           require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
           _safeTransferFrom(from, to, tokenId, _data);
       }
    
       /**
        * @dev Safely transfers the ownership of a given token ID to another address
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * Requires the msg.sender to be the owner, approved, or operator
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        * @param _data bytes data to send along with a safe transfer check
        */
       function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal {
           _transferFrom(from, to, tokenId);
           require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
       }
    
       /**
        * @dev Returns whether the specified token exists.
        * @param tokenId uint256 ID of the token to query the existence of
        * @return bool whether the token exists
        */
       function _exists(uint256 tokenId) internal view returns (bool) {
           address owner = _tokenOwner[tokenId];
           return owner != address(0);
       }
    
       /**
        * @dev Returns whether the given spender can transfer a given token ID.
        * @param spender address of the spender to query
        * @param tokenId uint256 ID of the token to be transferred
        * @return bool whether the msg.sender is approved for the given token ID,
        * is an operator of the owner, or is the owner of the token
        */
       function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
           require(_exists(tokenId), "ERC721: operator query for nonexistent token");
           address owner = ownerOf(tokenId);
           return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
       }
    
       /**
        * @dev Internal function to safely mint a new token.
        * Reverts if the given token ID already exists.
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _safeMint(address to, uint256 tokenId) internal {
           _safeMint(to, tokenId, "");
       }
    
       /**
        * @dev Internal function to safely mint a new token.
        * Reverts if the given token ID already exists.
        * If the target address is a contract, it must implement `onERC721Received`,
        * which is called upon a safe transfer, and return the magic value
        * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
        * the transfer is reverted.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        * @param _data bytes data to send along with a safe transfer check
        */
       function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
           _mint(to, tokenId);
           require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
       }
    
       /**
        * @dev Internal function to mint a new token.
        * Reverts if the given token ID already exists.
        * @param to The address that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _mint(address to, uint256 tokenId) internal {
           require(to != address(0), "ERC721: mint to the zero address");
           require(!_exists(tokenId), "ERC721: token already minted");
    
           _tokenOwner[tokenId] = to;
           _ownedTokensCount[to].increment();
    
           emit Transfer(address(0), to, tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use {_burn} instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(address owner, uint256 tokenId) internal {
           require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
    
           _clearApproval(tokenId);
    
           _ownedTokensCount[owner].decrement();
           _tokenOwner[tokenId] = address(0);
    
           emit Transfer(owner, address(0), tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(uint256 tokenId) internal {
           _burn(ownerOf(tokenId), tokenId);
       }
    
       /**
        * @dev Internal function to transfer ownership of a given token ID to another address.
        * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _transferFrom(address from, address to, uint256 tokenId) internal {
           require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
           require(to != address(0), "ERC721: transfer to the zero address");
    
           _clearApproval(tokenId);
    
           _ownedTokensCount[from].decrement();
           _ownedTokensCount[to].increment();
    
           _tokenOwner[tokenId] = to;
    
           emit Transfer(from, 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.
        *
        * This function is deprecated.
        * @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)
           internal returns (bool)
       {
           if (!to.isContract()) {
               return true;
           }
    
           bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data);
           return (retval == _ERC721_RECEIVED);
       }
    
       /**
        * @dev Private function to clear current approval of a given token ID.
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _clearApproval(uint256 tokenId) private {
           if (_tokenApprovals[tokenId] != address(0)) {
               _tokenApprovals[tokenId] = address(0);
           }
       }
    }
    
    /**
    * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract IERC721Enumerable is IERC721 {
       function totalSupply() public view returns (uint256);
       function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId);
    
       function tokenByIndex(uint256 index) public view returns (uint256);
    }
    
    /**
    * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721Enumerable is Context, ERC165, ERC721, IERC721Enumerable {
       // Mapping from owner to list of owned token IDs
       mapping(address => 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;
    
       /*
        *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
        *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
        *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
        *
        *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
        */
       bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
    
       /**
        * @dev Constructor function.
        */
       constructor () public {
           // register the supported interface to conform to ERC721Enumerable via ERC165
           _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
       }
    
       /**
        * @dev Gets the token ID at a given index of the tokens list of the requested owner.
        * @param owner address owning the tokens list to be accessed
        * @param index uint256 representing the index to be accessed of the requested tokens list
        * @return uint256 token ID at the given index of the tokens list owned by the requested address
        */
       function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
           require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
           return _ownedTokens[owner][index];
       }
    
       /**
        * @dev Gets the total amount of tokens stored by the contract.
        * @return uint256 representing the total amount of tokens
        */
       function totalSupply() public view returns (uint256) {
           return _allTokens.length;
       }
    
       /**
        * @dev Gets the token ID at a given index of all the tokens in this contract
        * Reverts if the index is greater or equal to the total number of tokens.
        * @param index uint256 representing the index to be accessed of the tokens list
        * @return uint256 token ID at the given index of the tokens list
        */
       function tokenByIndex(uint256 index) public view returns (uint256) {
           require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
           return _allTokens[index];
       }
    
       /**
        * @dev Internal function to transfer ownership of a given token ID to another address.
        * As opposed to transferFrom, this imposes no restrictions on msg.sender.
        * @param from current owner of the token
        * @param to address to receive the ownership of the given token ID
        * @param tokenId uint256 ID of the token to be transferred
        */
       function _transferFrom(address from, address to, uint256 tokenId) internal {
           super._transferFrom(from, to, tokenId);
    
           _removeTokenFromOwnerEnumeration(from, tokenId);
    
           _addTokenToOwnerEnumeration(to, tokenId);
       }
    
       /**
        * @dev Internal function to mint a new token.
        * Reverts if the given token ID already exists.
        * @param to address the beneficiary that will own the minted token
        * @param tokenId uint256 ID of the token to be minted
        */
       function _mint(address to, uint256 tokenId) internal {
           super._mint(to, tokenId);
    
           _addTokenToOwnerEnumeration(to, tokenId);
    
           _addTokenToAllTokensEnumeration(tokenId);
       }
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use {ERC721-_burn} instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned
        */
       function _burn(address owner, uint256 tokenId) internal {
           super._burn(owner, tokenId);
    
           _removeTokenFromOwnerEnumeration(owner, tokenId);
           // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
           _ownedTokensIndex[tokenId] = 0;
    
           _removeTokenFromAllTokensEnumeration(tokenId);
       }
    
       /**
        * @dev Gets the list of token IDs of the requested owner.
        * @param owner address owning the tokens
        * @return uint256[] List of token IDs owned by the requested address
        */
       function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
           return _ownedTokens[owner];
       }
    
       /**
        * @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 {
           _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
           _ownedTokens[to].push(tokenId);
       }
    
       /**
        * @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 = _ownedTokens[from].length.sub(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
           _ownedTokens[from].length--;
    
           // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
           // lastTokenId, or just over the end of the array if the token was the last one).
       }
    
       /**
        * @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.sub(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
           _allTokens.length--;
           _allTokensIndex[tokenId] = 0;
       }
    }
    
    /**
    * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
    * @dev See https://eips.ethereum.org/EIPS/eip-721
    */
    contract IERC721Metadata is IERC721 {
       function name() external view returns (string memory);
       function symbol() external view returns (string memory);
       function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    
    contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata {
       // Token name
       string private _name;
    
       // Token symbol
       string private _symbol;
    
       // Optional mapping for token URIs
       mapping(uint256 => string) private _tokenURIs;
      
       
       //Optional mapping for IPFS link to canonical image file
       mapping(uint256 => string) private _tokenIPFSHashes;
    
       /*
        *     bytes4(keccak256('name()')) == 0x06fdde03
        *     bytes4(keccak256('symbol()')) == 0x95d89b41
        *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
        *
        *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
        */
       bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
    
       /**
        * @dev Constructor function
        */
       constructor (string memory name, string memory symbol) public {
           _name = name;
           _symbol = symbol;
    
           // register the supported interfaces to conform to ERC721 via ERC165
           _registerInterface(_INTERFACE_ID_ERC721_METADATA);
       }
    
       /**
        * @dev Gets the token name.
        * @return string representing the token name
        */
       function name() external view returns (string memory) {
           return _name;
       }
    
       /**
        * @dev Gets the token symbol.
        * @return string representing the token symbol
        */
       function symbol() external view returns (string memory) {
           return _symbol;
       }
    
       /**
        * @dev Returns an URI for a given token ID.
        * Throws if the token ID does not exist. May return an empty string.
        * @param tokenId uint256 ID of the token to query
        */
       function tokenURI(uint256 tokenId) external view returns (string memory) {
           require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
           return _tokenURIs[tokenId];
       }
       
       
         /**
        * @dev Returns an URI for a given token ID.
        * Throws if the token ID does not exist. May return an empty string.
        * @param tokenId uint256 ID of the token to query
        */
       function tokenIPFSHash(uint256 tokenId) external view returns (string memory) {
           require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
           return _tokenIPFSHashes[tokenId];
       }
    
       /**
        * @dev Internal function to set the token URI for a given token.
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to set its URI
        * @param uri string URI to assign
        */
       function _setTokenURI(uint256 tokenId, string memory uri) internal {
           require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
           _tokenURIs[tokenId] = uri;
       }
       
          /**
        * @dev Internal function to set the token IPFS hash for a given token.
        * Reverts if the token ID does not exist.
        * @param tokenId uint256 ID of the token to set its URI
        * @param ipfs_hash string IPFS link to assign
        */
       function _setTokenIPFSHash(uint256 tokenId, string memory ipfs_hash) internal {
           require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
           _tokenIPFSHashes[tokenId] = ipfs_hash;
       }
       
       
    
       /**
        * @dev Internal function to burn a specific token.
        * Reverts if the token does not exist.
        * Deprecated, use _burn(uint256) instead.
        * @param owner owner of the token to burn
        * @param tokenId uint256 ID of the token being burned by the msg.sender
        */
       function _burn(address owner, uint256 tokenId) internal {
           super._burn(owner, tokenId);
    
           // Clear metadata (if any)
           if (bytes(_tokenURIs[tokenId]).length != 0) {
               delete _tokenURIs[tokenId];
           }
       }
    }
    
    
    /**
    * @title Full ERC721 Token
    * @dev This implementation includes all the required and some optional functionality of the ERC721 standard
    * Moreover, it includes approve all functionality using operator terminology.
    *
    * See https://eips.ethereum.org/EIPS/eip-721
    */
    contract ERC721Full is ERC721, ERC721Enumerable, ERC721Metadata {
       constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
           // solhint-disable-previous-line no-empty-blocks
       }
    }
    
    
    contract NiftyBuilderInstance is ERC721Full {
    
       //MODIFIERS
    
       modifier onlyValidSender() {
           NiftyRegistry nftg_registry = NiftyRegistry(niftyRegistryContract);
           bool is_valid = nftg_registry.isValidNiftySender(msg.sender);
           require(is_valid==true);
           _;
       }
    
       //CONSTANTS
    
       // how many nifties this contract is selling
       // used for metadat retrieval
       uint public numNiftiesCurrentlyInContract;
    
       //id of this contract for metadata server
       uint public contractId;
    
       //baseURI for metadata server
       string public baseURI;
    
    //   //name of creator
    //   string public creatorName;
    
       string public nameOfCreator;
    
       //nifty registry contract
       address public niftyRegistryContract = 0x6e53130dDfF21E3BC963Ee902005223b9A202106;
    
       //master builder - ONLY DOES STATIC CALLS
       address public masterBuilderContract = 0x6EFB06cF568253a53C7511BD3c31AB28BecB0192;
    
       using Counters for Counters.Counter;
    
       //MAPPINGS
    
       //mappings for token Ids
       mapping (uint => Counters.Counter) public _numNiftyMinted;
       mapping (uint => uint) public _numNiftyPermitted;
       mapping (uint => string) public _niftyIPFSHashes;
       mapping (uint => bool) public _IPFSHashHasBeenSet;
    
       //EVENTS
    
       //purchase + creation events
       event NiftyPurchased(address _buyer, uint256 _amount, uint _tokenId);
       event NiftyCreated(address new_owner, uint _niftyType, uint _tokenId);
    
       //CONSTRUCTOR FUNCTION
    
       constructor(
           string memory _name,
           string memory _symbol,
           uint contract_id,
           uint num_nifties,
           uint[] memory nifty_quantities,
           string memory base_uri,
           string memory name_of_creator) ERC721Full(_name, _symbol) public {
    
           //set local variables based on inputs
           contractId = contract_id;
           numNiftiesCurrentlyInContract = num_nifties;
           baseURI = base_uri;
           nameOfCreator = name_of_creator;
           //offset starts at 1 - there is no niftyType of 0
           for (uint i=0; i<(num_nifties); i++) {
               _numNiftyPermitted[i+1] = nifty_quantities[i];
           }
       }
       
       function setNiftyIPFSHash(uint niftyType, 
                                string memory ipfs_hash) onlyValidSender public {
            //can only be set once
            if (_IPFSHashHasBeenSet[niftyType] == true) {
                revert("Can only be set once");
            } else {
                _niftyIPFSHashes[niftyType] = ipfs_hash;
                _IPFSHashHasBeenSet[niftyType]  = true;
            }
        }
    
       function isNiftySoldOut(uint niftyType) public view returns (bool) {
           if (niftyType > numNiftiesCurrentlyInContract) {
               return true;
           }
           if (_numNiftyMinted[niftyType].current() > _numNiftyPermitted[niftyType]) {
               return (true);
           } else {
               return (false);
           }
       }
    
       function _giftNifty(address collector_address, 
                          uint niftyType) private {
           //master for static calls
           BuilderMaster bm = BuilderMaster(masterBuilderContract);
           _numNiftyMinted[niftyType].increment();
           //check if this nifty is sold out
           if (isNiftySoldOut(niftyType)==true) {
               revert("Nifty sold out!");
           }
           //mint a nifty
           uint specificTokenId = _numNiftyMinted[niftyType].current();
           uint tokenId = bm.encodeTokenId(contractId, niftyType, specificTokenId);
           string memory tokenIdStr = bm.uint2str(tokenId);
           string memory tokenURI = bm.strConcat(baseURI, tokenIdStr);
           string memory ipfsHash = _niftyIPFSHashes[niftyType];
           //mint token
           _mint(collector_address, tokenId);
           _setTokenURI(tokenId, tokenURI);
           _setTokenIPFSHash(tokenId, ipfsHash);
           //do events
           emit NiftyCreated(collector_address, niftyType, tokenId);
       }
       
       function createNifties(address collector_address, 
                          uint[] memory listOfNiftyTypes) onlyValidSender public {
            //loop through array and create nifties
            for (uint i=0; i < listOfNiftyTypes.length; i++) {
                _giftNifty(collector_address,listOfNiftyTypes[i]);
            }
                              
        }
    
    }
    
    contract NiftyRegistry {
       function isValidNiftySender(address sending_key) public view returns (bool);
       function isOwner(address owner_key) public view returns (bool);
    }
    
    contract BuilderMaster {
       function getContractId(uint tokenId) public view returns (uint);
       function getNiftyTypeId(uint tokenId) public view returns (uint);
       function getSpecificNiftyNum(uint tokenId) public view returns (uint);
       function encodeTokenId(uint contractId, uint niftyType, uint specificNiftyNum) public view returns (uint);
       function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) public view returns (string memory);
       function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) public view returns (string memory);
       function strConcat(string memory _a, string memory _b, string memory _c) public view returns (string memory);
       function strConcat(string memory _a, string memory _b) public view returns (string memory);
       function uint2str(uint _i) public view returns (string memory _uintAsString);
    }
    
    /**
    * Contracts and libraries below are from OpenZeppelin, except nifty builder instance
    **/
    
    
    /**
    * @dev Wrappers over Solidity's arithmetic operations with added overflow
    * checks.
    *
    * Arithmetic operations in Solidity wrap on overflow. This can easily result
    * in bugs, because programmers usually assume that an overflow raises an
    * error, which is the standard behavior in high level programming languages.
    * `SafeMath` restores this intuition by reverting the transaction when an
    * operation overflows.
    *
    * Using this library instead of the unchecked operations eliminates an entire
    * class of bugs, so it's recommended to use it always.
    */
    library SafeMath {
       /**
        * @dev Returns the addition of two unsigned integers, reverting on
        * overflow.
        *
        * Counterpart to Solidity's `+` operator.
        *
        * Requirements:
        * - Addition cannot overflow.
        */
       function add(uint256 a, uint256 b) internal pure returns (uint256) {
           uint256 c = a + b;
           require(c >= a, "SafeMath: addition overflow");
    
           return c;
       }
    
       /**
        * @dev Returns the subtraction of two unsigned integers, reverting on
        * overflow (when the result is negative).
        *
        * Counterpart to Solidity's `-` operator.
        *
        * Requirements:
        * - Subtraction cannot overflow.
        */
       function sub(uint256 a, uint256 b) internal pure returns (uint256) {
           return sub(a, b, "SafeMath: subtraction overflow");
       }
    
       /**
        * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
        * overflow (when the result is negative).
        *
        * Counterpart to Solidity's `-` operator.
        *
        * Requirements:
        * - Subtraction cannot overflow.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           require(b <= a, errorMessage);
           uint256 c = a - b;
    
           return c;
       }
    
       /**
        * @dev Returns the multiplication of two unsigned integers, reverting on
        * overflow.
        *
        * Counterpart to Solidity's `*` operator.
        *
        * Requirements:
        * - Multiplication cannot overflow.
        */
       function mul(uint256 a, uint256 b) internal pure returns (uint256) {
           // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
           // benefit is lost if 'b' is also tested.
           // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
           if (a == 0) {
               return 0;
           }
    
           uint256 c = a * b;
           require(c / a == b, "SafeMath: multiplication overflow");
    
           return c;
       }
    
       /**
        * @dev Returns the integer division of two unsigned integers. Reverts on
        * division by zero. The result is rounded towards zero.
        *
        * Counterpart to Solidity's `/` operator. Note: this function uses a
        * `revert` opcode (which leaves remaining gas untouched) while Solidity
        * uses an invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        */
       function div(uint256 a, uint256 b) internal pure returns (uint256) {
           return div(a, b, "SafeMath: division by zero");
       }
    
       /**
        * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
        * division by zero. The result is rounded towards zero.
        *
        * Counterpart to Solidity's `/` operator. Note: this function uses a
        * `revert` opcode (which leaves remaining gas untouched) while Solidity
        * uses an invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           // Solidity only automatically asserts when dividing by 0
           require(b > 0, errorMessage);
           uint256 c = a / b;
           // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
           return c;
       }
    
       /**
        * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
        * Reverts when dividing by zero.
        *
        * Counterpart to Solidity's `%` operator. This function uses a `revert`
        * opcode (which leaves remaining gas untouched) while Solidity uses an
        * invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        */
       function mod(uint256 a, uint256 b) internal pure returns (uint256) {
           return mod(a, b, "SafeMath: modulo by zero");
       }
    
       /**
        * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
        * Reverts with custom message when dividing by zero.
        *
        * Counterpart to Solidity's `%` operator. This function uses a `revert`
        * opcode (which leaves remaining gas untouched) while Solidity uses an
        * invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        * - The divisor cannot be zero.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
           require(b != 0, errorMessage);
           return a % b;
       }
    }
    
    /**
    * @dev Collection of functions related to the address type
    */
    library Address {
       /**
        * @dev Returns true if `account` is a contract.
        *
        * This test is non-exhaustive, and there may be false-negatives: during the
        * execution of a contract's constructor, its address will be reported as
        * not containing 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.
        */
       function isContract(address account) internal view returns (bool) {
           // This method relies in extcodesize, which returns 0 for contracts in
           // construction, since the code is only stored at the end of the
           // constructor execution.
    
           // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
           // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
           // for accounts without code, i.e. `keccak256('')`
           bytes32 codehash;
           bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
           // solhint-disable-next-line no-inline-assembly
           assembly { codehash := extcodehash(account) }
           return (codehash != 0x0 && codehash != accountHash);
       }
    
       /**
        * @dev Converts an `address` into `address payable`. Note that this is
        * simply a type cast: the actual underlying value is not changed.
        *
        * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
        * @dev Get it via `npm install @openzeppelin/contracts@next`.
        */
       function toPayable(address account) internal pure returns (address payable) {
           return address(uint160(account));
       }
    
       /**
        * @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");
    
           // solhint-disable-next-line avoid-call-value
           (bool success, ) = recipient.call.value(amount)("");
           require(success, "Address: unable to send value, recipient may have reverted");
       }
    }
    
    /**
    * @title Counters
    * @author Matt Condon (@shrugs)
    * @dev Provides counters that can only be incremented or decremented by one. 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;`
    * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
    * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
    * directly accessed.
    */
    library Counters {
       using SafeMath for uint256;
    
       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 {
           // The {SafeMath} overflow check can be skipped here, see the comment at the top
           counter._value += 1;
       }
    
       function decrement(Counter storage counter) internal {
           counter._value = counter._value.sub(1);
       }
    }