ETH Price: $2,547.68 (-4.54%)

Transaction Decoder

Block:
8813746 at Oct-26-2019 06:23:02 AM +UTC
Transaction Fee:
0.003960816 ETH $10.09
Gas Used:
2,640,544 Gas / 1.5 Gwei

Emitted Events:

7 OpenSeaAsset.OwnershipTransferred( previousOwner=0x00000000...000000000, newOwner=[Receiver] OpenSeaCollection )
8 OpenSeaAsset.OwnershipTransferred( previousOwner=[Receiver] OpenSeaCollection, newOwner=[Sender] 0xd1898665a01a91ac10bd2c6cb1899336df34ac33 )

Account State Difference:

  Address   Before After State Difference Code
0x24e4b9bF...60d5e3BFB
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 311299017255145097793712059764250426619868415484111826446423363607306962983261852755876868586578625230266379176184377235494241930026634858684790027600756709323877029481791635634940166937834334018091336980916500342528941338759767153815773385838906078969416046516331963618356130067145233459695923578459627789784928225452107905492230786815091364724668252224718620051934765073801066756710850559192598524285664606862462813891795148430288740333800253828454726564900923949671328341796870506779732488773553668060005529628343448110629234676425764014164067335478444014077206727246701885176686085921077507272532046176212765064478483337630379673741628922984601255007708395819370634054138713893310381166899794390088610991512233406887252461526127242694933992932419657963578418772521227997903816684253532192211660971470090349570733840677275864439808111840590182964232029947292838108521320887033909880062692870934651162321240654253817589542505103153997028629478106128698861137947900252137100557596312685122088580624576080682608484735238379832138115779926264635464010619335689566059980324315874183393931468909619334803516695063872888540264265478762189290022752234093878966387265502530310981284100644043749065398692806760957090017971453490816605956018198506232176437114715006961590417544574321685851793612759910486802118045857305986106325495601281144961648072941715457706665776735767243203255061807813536123515419033072430438738328595571095972394590917641364112728089481163584845472896639408998584802351525365463105539074554411132442238674223271199797661786505051333806827349845130685599162474481166445319880855398638152226389765584280377281642454956144441003295136019881848389577364637894413621747148780371164002040580534127510504324919729022233021139350342608570477231203087349508084074249155546891833208627262423739933040023365302521487385286435911300836487924792447536909842143506145463080312212518720170557059155828880145937735759053351577410576231359817301876734671183591326938546266555794295133242656236569471768984898744586533588437482231804001298062461177067735706756340770857490878735023686023938444647421852654186023136320257651382101554902841342617274852892841060996889479001841800810741213563508521250272937078139023856220178565562630324106871560620015685903830772699022364888891557834223559799421169329605269805767794300712703727092362299062624162218797588498831966336131093405769373565116646931707041940183635835759802949975060850350713612336022291796963106961852682639186980678724206415618723218797116283752076227525267284039003689971549579086286679737788722367358518551699727720315721762193666800608502759511808283851141834208348001816468230035897505792509535901812027946918298139394982391012274827634689474087147801201213622547159045652461601698467314093445635598896119770949102229015600208251275634471858803647013752513526690983473146083833968358145556325225316237790968419852632263247110823789922742401970117710638227004334662024062177205747278625263680129744678188501988447527186370960176288073192498569706629298983659655266114057503624096950805226284880491664234522500250244782959206706765722598570666724331448688161349984903254447139663614427807834079038207339079001547872894529452395928985393738174075696506972553805733098391888283435377213448321293179960646281832810085859616558899162893506069216518878888375890624460013708324446759186375560767663411813323483758378753802214412180783360192822267458794693973224035220041870321683771655334305569947820644717700432876149683136725501636930281120123158697089697340131358926350745445170652963714865407725093036941381500890781814769701247770518869154638766348415176596851617635157154325732051211385002440757970414798178536710921340297454392353818327453161336314196646945332454796580213861736240204453402291354063675803080076819054502829672936536803499262389947293247699323342594310165780574405906472524467494305782620214217849525053970735005409787317199625857417393869695474393922524970975785136680330808293672116795822691739575096678822254820573793254687570093365869829052600097758776029489189694214893065892593038849305297793684185039557879973241052166892580342485413672814063471749264576559924155882228573496808793006703379537253078924082399558336519028747870950505926225306268038069298644726164479423150662066752188695701956010443628427787490638708602153463241256123956468771185221037799105760647606064258690519290202787332173425979839339723067086203557868077215877659631263657525675858694549823494148843287564929927694491044056770945530725093687368524756964536065125952444471205874887627340501784178633148346873086887361774456979229331053864250820957651366314359404372517026018595031548357712399542661643265294421818413138422439333155564207255507965454824465418989923329860297898649621883830203936255827580613722387166230997386326523195052011824887775216650377539785080613867148527565971078692155161782055304875280112487261615366987975167560429553454724539562584993320302413505471098449564626083181583243403108330456691554893690078436665274745513255000834458889031048155071562666724256689055304126951649696060175264839498276375369070317600820979295960654891836199550840573782678246774625267335564235240461348198978007652188040762692649541960765628934477773002151791770302494571678738547770402944553082004268774972019801594828908959576371928820070828305819166648956849884836603983556190383504796481927735511417660607252944921478736355758072344763001281556166481153033090875198514550128220798953347415549875970070982606502461489732777467485452718726452476581514902493677636096966396980412009877388634411454277718330316891829272858120796077143508461203105773431803001691609529036592780633437359025619812631860165311538238819779594914456971006566648092536583415295242754147587372460955595811303044424275529123512484933955652681363045271856227450033932325381463718993515765615232960934089214520851552002083188076027108025491110906966178947708010055439864818935577116020157983221993638400880601805566324392570339872087545854821206953619373331180725010737399995643286757675550986273602180864330442766435658223617011897116728665779157351704254900946115261635093247019115691037094595011918215883340079377704979565127791429891177701582632788229352131958649000282587511903360300525948734852282664043749363273955056284199436107757720168365570204716956990589435965905349985095243486517747237938038651759203517711913504674019367600446541089670437161908623501933993020825614211898034161658374516552577490356640332142283199931217140196570949012801313816144623874205321303049221325362741963282857714272568426924976463883720548924811281872653637395300180985028415346393389783836801699145413562711928518562634473923338413343810213787095883639282871784757488699234580406977920820296855928009733232924643755085704034187541325744216472764017927422919527294357014482575990334830841701484883381024073490682717620325182277417486956920640863863384654555703671909146139259350275224854487271634893148686649167701657458419200085294611423424578142402880821080715863857248214166454444206830136930599825911391896321114000691354183110702183744010678258835963125941674837007050069177485990806694668355019449342914621926727367662430217477054784080303931583626182577996179756636191475907707397478201486978197539168067812406794905472567952627895885657204934603677967487262538644310941631616090953166557515063806766856540690846929803173900502280001497906930094526379569018494793097188173117823075414560008672890222783684825945909185323669049012987412731898664946664390298740336127510973171484006838035862359473632538811706714349906155414275092599506821583422056172816970926888280576989682545571598609142613025649167670601189006317514690557157270800130523545459789641421079548329797844258557869587077887015602797593205089022015613409819435852851329201943631947952788959200090902865601922035311681667238380515899558611774427434912359551486317482973276889183635289124816119982000327810708819992684800811587221984377097051629876106615828452029442085176727674244780013893662801116626712318892907253634687317679352789800276270445937438539598188951801427756666093030684101097693644885162006597870919160726723296275382622367125371928843346164693682026269660716409559373661833874441774169156874463519737310471927460644370759533783292327930122563138824612484529178022478636925336624639140114812224717111832657209918462105924952840983125214850694365550394702330332573293998143292830020899353094707217914924266464748073625314089388869153962766623579819037374140299899443503708935304169216359544573383810672624628368192811678858483211775165639374142163610808320839407695886299096311470127963511126724334674333228440913517760235357876689934930676104237460072255008002146325380438484884887583266669666255309752808723940243313806922100785085626746669021661747964097854429413824639727796784194020187128095658394597186917616611545506790222975557041141614331968790146068631789664461125191422013327529604497605802127555065479418683929965978270462851476793671489955683212022031308114425835859849631118960104660227827142010081173803173788036905570366425106106222784556757554503347436306590035392963524296182773615100588703001825191880961800060221280137945094798203059149798665784190366924117522154703521938826665590338332790888307401483072384026950932748227486207124223987532149297506275731304993688937781643892568095532473835712565881068448594020290759281645136845311313676815913434263452541060401699134405191308939260323826701398227703559574030252193936371076704953742513273276410530104027379509235298379851129975480975993251101631975366121777184304519226204074595693425007295669212929602226481409654489666673046978442586616884034800786874987869800087414003534346911747308346225792721276092089017170939165897944241431610671678864024722834235624496192695495868128553936976953175153476637952703402002827742347877393834922414615307113049286345047317862633948213734908070045398129756014887214170147132269285244308988662320744053152788991908944587728732512866119297734866141219908530879553482720508455392574252420923356293980946328440293212884092188333369243107618157345625671515485191613591007680472024351428667047697492962820647976763845584289495320952901281270884128509887891523791621914483002867210213307389644045519598945202572506282101804841153586598373677395240917802409426184769112761079692552783211338994985571591802429243982659840430517972134022610637176101739030535810126904992951790699460552112909250924129286891465560848521420181453255147837756585886789421515048208597645613180066713266832123453088600327285448488611184201229705616832772113081376693621165045737309881454709630030280065948576078791162610667725482209284724695402360855223623082652981167740749732391215279382774604363011292455347154147088851559613780428486355711271779624971978988876875240739058208046970475190027791465122715334281573947355016442431784286058662825412863050226555005717202787718797639084759888008636458326948997701936817725732949118219731303968554916081676195950978708221529692862673021115452509562910917988201303707470597297830227478135214703149219343349607941614563212512422310812178092556933865279412819508113280130689376401178986986428195984029188916606481503673474067618341576922322755991809649647583502456997679731910892767426202490808243828851840032103109917115983049416273954683219163067258639806450308485990743829678814639423130180117728126229229401502473191824185795405699326909562925176324435501070760594511781591262568073846918520107053003526855321813901264106242562928879422089880334535326297441989211653353327315993055184266529554990335935092614971051132244305500567834140419549126749060142751003948602910257872270774640979161234211134458256323637740419046047369893177372766507453723662095953769042945067775393576737816054404772250414809405110317403707288663839689213839137444602389945928336514055377547759640164625900955630870374217043162473679875489388287369392897473998745120904344624088847765460139371275073256350683537255456410925162220054036648827545280787180933652034332306140319535382937142810349669604613676267182450547723485534020305112748181269962381686939630783972293525656837850859848947655519489438693396235059128671901003248297848842593522649551825103760345047754917091904997284315732698960272249208446449012921002603834447581695636405313388544786670573908116772529053902906315512492971647137832938484113706396827563534728191977049064975042478786201182449891763122783469342810581371295198154360626304345661406848582757027322690257650137684293311266363664193379861705346177509568810982965873771828904284398856495088713286493778637616193556458241210351058392130429566988550629569459960619720410587566622798542229769134467100836887844068213241874441203190863136747381399883044642611999296790467752237650675092278583993187762107842273227908152267378260013989715682703026351056945597236315081641152751226044755241697735197145219435535122419300714858882140621004895912138972778139173611959576082574536743796730182620189439828348460447052504018742294913702555158764677422960469112611378530010923324637760401405399075909665871012733354021374592888678178139522391478065892319823073532327268553272547797274508559475312936549278328870222741706639533996194386159757086692321281150880169583630478165854816496220521689316484097560363516072397869797532554664448146232442794151932948267488357346299692975937850120994972301594644471672217633930311000485006596238963799781422415388895497336840663130691880683788804893339373306298614985326968696262693972305216909110345084441947611380470896521616589190749894610378485144767997770331050958559924299152693682773758635900707277955244905536247986449583252226734196409380272726444532248350787339919171719540478420445038617206836111795914925666578928069863472253034638760563445883665440290473375997631034383281102230706442239937202742552491446023482249585160681399342363876966385889368167325885236108633269295259551650324586060499862320982675310468931831693148151700158226668611254137075355518876889166903278948540983512516236746825936590979165139253528291902445096532210776320909400905702530678314956297770440444475864941749314614264081743833656836720318652962802234992300662559835217394534211796103699749869276178198446439857340114009740009811392370123763238447919663215827009147825762139454172015541145030342227161891313922221943010521400638090394054522735617137361064352403927501594596255169891269476943984730095527158027663685192368321382571235727077876065598289667005911069059861819380988677184388604856654801008145337730750917392467900892548264772385795450763894687866351024301470366117422801588843324849346919805949936954530240135981663652826973622846801780850711421927096331594655665710542535773899678930812633488319945944340694744057888404970637736082210250353339441906938207345978025008833058318518466268365099245585888392688349241361131957232619398008796086802429950728239053605176394919779977176418224289897994120620321983638264092533118331474575596566868644788774079151643563549320058230118704308823822112487352689192866244063639506656214261900773272828074788786483036831293232359135497399620078959500270787557331365840010257953026676541836894988650741261848601001569304071831142916884966656392580274998764728431118484411815287544304425025445664118272333942097651070797621320863173144874377391474249570137777484159508637364685945555956003015633136591358749514226286313800819619805680565775631675358728485852165622753466278658425852335872338650864182068082287234318653435354809469257935667750846721461940857974870865061894511329877356470232929118717889179165423499829370436974841654696063743781576423853335025980888588298910281730314076081735364453204350163405220285292954103207611240326741542186753932433906427885416421054159988427212575327645570192861042947868727542068699110877866172966761208674228160563394816667684595315164569578683390905297227068711374960970016343090024855648540774120960909882036009126467435361708625350417146334461405905888589948136549899056787237123492814818978616964164920520471223274617010077215956405408121714128661069761578931669086130424446760819000267527139187675330358121821566032949639586612563320426729529953422975241106057328028906040327682770207231434685683026112936679367146005955753693101011981787331342009251416641408910558868918782559159565498608611911751620194419741362876458819191418035131038244741122780818949999476454200591502526539643902666301450384472893110703642384213703669759735857022271300086487415700899879684978312742293470794854147752088614920020436738479361557826828367040144058834310852508789234408301303674340390967407172054838826579009202270994045775362309284771535146956223918902180450648672129265378247558379667980975913156712216673131923941880901710309448449857671974421780300944274330865130110526627428988038737972723158589819960739610200527149491020811039649324019011201088831262860176492248751864040594706065598849329454063654487131439206627068260222822873330913768836168003815160157773317881166954015083836406442662423582456970396151006922803702297155121261404046519728957598454996697445876552197794878471485170926070458335419413223405469316959657143660857643511951646602011898968431855593533492769073241465503504099940395720816913657433221762352902863893034494351271530056872194900642356285628662560225896142130672445466077217500572361032277598391622846307448174888931464757743731532013765386219014615881767301978010338165412216195614098591325794643676031314811122607370461817197119176163454829374080825896941827193992459752677540935888858389795328867991458015627520476762172971796095377916639350144557685030675515110827028662056375806350007509940559738965330228258582195213541193574010038958600466758727573367785350614464363008872348192568205946177274197198636357660423392009859779626613818032213213649919158014486659304210724585064836276069631389120922055590554462555565704808989081556204165941310858820273063377733864060102482864360976577981143953288836044914539031330420304382916809510499497818813348831367125932178865206566388965898242371099014075704914997832229575824463834936081726977254423666756882459933985732327295549176291131125205208604294669148294154657896755117454242006249846794983522918929174776153873223194398120443970301573643176768749756679219447304532195547618852495141153863971852168691595548783967253227979300299937587360029910607831035208155831054357203696284957311787829305780854255286593844760643290451047509922395096562987491354556193397620271916794678654464695717323112699191986392438405155366177168506319708828255381342340948456468399508472558282469969331974811484916515281944081307261088790053870462432749654581904484606501760558547299829712008322793532950085559882014718487121117670606589800954015607732730141479861783023362303062432967233243187096187761624227196663736608470711397214465907266131459082884324940637975721492085211912063507016229912891023456852318175603188595193804852346845235129567941238652686842212427037057374544892152218856491715809297946542313370634745332958256609035740372047109305987883336955723324418109386891149373785066681309140360062151884847586957891913750118159158290469699176846406064074511315889781398402702248466639883187887815183652257251355160632482928266881538787538070051342670037865648371293116993857631741702180026440468443370705922021657968919414911191359005027947801454829692304070424275612784400369615174147460113661099709465084853514206300425542936127576463018151905914086217588367722178656663791729958317652692396994648110792735449491744054272366497282895375500742465730353057260475371190524475069715404056770594956627041161095452091080752356762621105362487181218928600689232610112107239174926803488440482961514792907162685539358816458093570033597026624730970842856028352229788120765596922532563603833480940851673065761998201622359004405597463571605222625578440198684316700247830224903602258503543634624886785864521780882048488226266707803173057422136822117101664314661778982191485547765884163260599718429138205549984465423493029137581995599596909912131536288058911097094229891233205956510474899904138396757572116465778981408072655063077087746145010608377760013501279867735417383232763580980650345998932607272507360577764003263850671803290237625239910981740730004449428273892693088165441720814273880995471289795971520392545880633439742060215413661770638696791898314092327332389952562149045560264153652885711613202196993532455509817581784069350484457559687606530732339736660312426105299795675515339549270410642055336157324595316694934416790494671200077808856234007348711798874474350775656551601958067605289662628175537523008707829760210657679150907870268170996605124134119409757063949696635295961393607087349600088421108680570027708284931108693949181554081816555249190386079526716355882602624287048077909717893802609377366755895843604836876819671475972882034225022400479003964326156014714741231188240734038296679178875851471775180932061413501166120671602711522743729838199247928857804205850426915361669774042231385867950870774906949155942513346456778875812461116995284351392254268489868390158678992610369885321809756395184579164770573628414071162377477555662836393999263998372750697726433479197483267647105937559853137322311149605770334326939456274657534596492383140949542269418913541567535746767104322097059531058117323107567121792610823445475629064184515283474989346474511197815625140137360139681699106532238609889816882291745605286796643120804546815967815299320313692917911752382071436960941653484299516800936213677733735584179130426923373710852175984170996024062876306526167066459804983460452070705968558658136122018632947916247532195023551425092726096103545279973821711653597034771600193248481846900081617607909318372072621852766445015754501445745416266986762708954579580105392236585329560740115315164334386254708681691605755059825108456838527557932486381830134791729302551241515624617872685243424429508862912262658531177010490564621386806328074045474615099705688019267587173573583930950939491730320713103348337579230744440670635007261634020946719091747241286500131824513848366385997020251327222139201521976957638747643058777717574383437457479975243269627861844585086300327696991834668058900885126062665298148941295137052793621534278275592260809394415808313104879018955213700119246713441838409327437325618538773141841303960650097394919622762831003439929968861098926650630970266832333313809573263219073646735275414984119788586627955520178059537818355871349181186954374364817779745965672837568458954511277754138509037975733300878945807105991968835436084891305676090719035352636746412931371320306853012726027159106455608472304277074646331460721025000408788775648456246376340910831120557384673442559475675716216494966361354970802636937434124050226552149888264871519705548331072050319598182027182388742445493334225383710561291359432407001210439698187529006039059212821131825497243629552827299669577522781787191194163226047204057146085428549392798968906969664767593988277705720627773642562196186876643955352250927370614563598135128880559919026848233081946751821706096190416874203923922471831105335133052526899131377133583105852455166902915191869742421292547516477334935490895325851327879938824951459548311814934746303007663162991587413437959054985636035879442807700363914638641326434772603292271092220069780632694835088743860091373922036670286999322640805020969176197251010801713604242396488587726049585407859922034511367169473930316607018938610889307485844012598422051246797909646386656504935925229657909500050808772647838474135518288113332531693135425043663760029962568094457603687874065041883793565063245422081672960653880760974847932139744317523164297688061064854183256616887329191780026559952572417288951162632612871331317560544036337909969580085011886347588815400326892758714895778139042424092763483405068466599169375584740611261260800254868669959886720024277989644490706314708442715014485515107778223984413261829008054691959555473801163685488597325395284499537921209734481267595092106210639499059614628796244896008170258003673527337996012035528526809751292798001025811147948796747721859658737668213175388361341547249502096519505086151545576137709717837896716200940031401228898881632905244621010912697222151774537958245315556972275786468499387351266046443987844430122122039353852199574629885129321122766625904655023776598869580031369900381286650159532170121756992279938591034994504059144560139725827444092970365493125031407701790162174107334942600669490008706424960937096248377227682736623375922437673762470392137792154090938396747423746822283565621842378734384658472043535417626749779595970673548274251166958352336948689001783739072561516290069426973095935882249700343257216824069203400787899070924070484935219557235062280763853497670602110780044782886772355837147367431863235936580236094409396959378401656311747803007855022268490730748282740378100460546980092373972837349075198587024845594532177401370149941587950068016452147961185019445730252967449248237369049252802016675490105159763431226693220137774770873421381844088989340733862373640142240711915904620461705004571101626973594367184034776764696077787584585980583053544211741162075879478738804273782027820174472456288634807024016912899471964846890860118929037246421105320360840552390073814144141206007283842259330137799588949252666024704029721288875910927605514580737807201923086266855576583238498472972031819929447869860240259101434176678694223356476995785479561804501964872998938094909382140854914016937661530536330289767361044999750544620897270935159655414764411976399253175230190268618600986573836492844921163509792982303175646253590062439474249344270306062880028049854593672788947867322423505651684759462295494809574120094992218470679420570896736634313872382890408071200842244637752449788012840313556566987451035293632022339425918521191192242975053806990444119570212290979360533201664067481950598120024841282299368298932754437763797606017458928053105294879609524797800720010516411084253472391956634182091402394890462020086261083749786378913234524096493816483242425507426254557467558085152014522777257157304017291584864367522114637980155110244414620925902769829498106742754794089489732781837715504045259283355096985652059837287192708065768139408768279798004786128450637405828444908960222175260502546230631627644585537327877762714396528528255077701456578947299735129511039829594780611303343790666450664440016305553014423300851318387025089924474704423724603128512852374305644566360033432137466544783200186493296078115302438782034304734369128723031011389271002758461178847034421759723442487389111210177215434311146647078633329200198015247293959976413825132368538422340381267847396399770432871213319167021996834196870646989804891435360994306482548289753727847926708933678947388588563975838770747858182899584029647730396515562493369700264455068428998224430973117905691504710380982811637661211553855911029881632235159229092867414453695154554970090849110441504934012776036556112471664052423313033279399423299272845885111923153782370930958974538043837165164607932656198710737558622290409034903400832536538331386818095662364342110206980847239571678920440232291204176601585787181735529000941457271030552509416847786666037780810276843820640328447516195852169485065111286413119010621231155638977942972660995139435524188388360693273532499288599490570242866394148981978651829187782211168831069317054139286543455519647845963357615010303776907993018306599843057932502370881125003096991756330002642284488207473088967229930964423918803359668548502897680722161675268576683202852156054061419363666288330028147329190276034281447255769673072825415366481673277912688598777897
13.292079528471855502 Eth13.296040344471855502 Eth0.003960816
0x957AB98a...54F2756f4
0xD1898665...6df34AC33
0.085271105271775198 Eth
Nonce: 52
0.081310289271775198 Eth
Nonce: 53
0.003960816

Execution Trace

OpenSeaCollection.make( name=Cats, symbol=CAT ) => ( 0x24e4b9bFf04F311E4029FDb8928c58F60d5e3BFB )
  • OpenSeaAsset.60806040( )
  • OpenSeaAsset.transferOwnership( newOwner=0xD1898665a01A91AC10bD2C6cb1899336df34AC33 )
    File 1 of 2: OpenSeaCollection
    // File: contracts/Strings.sol
    
    pragma solidity ^0.5.2;
    
    library Strings {
      // via https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
      function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        for (uint i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (uint i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (uint i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (uint i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        return string(babcde);
      }
    
      function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory) {
        return strConcat(_a, _b, _c, _d, "");
      }
    
      function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory) {
        return strConcat(_a, _b, _c, "", "");
      }
    
      function strConcat(string memory _a, string memory _b) internal pure returns (string memory) {
        return strConcat(_a, _b, "", "", "");
      }
    
      function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
          return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
          len++;
          j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (_i != 0) {
          bstr[k--] = byte(uint8(48 + _i % 10));
          _i /= 10;
        }
        return string(bstr);
      }
    
      function fromAddress(address addr) internal pure returns(string memory) {
        bytes20 addrBytes = bytes20(addr);
        bytes16 hexAlphabet = "0123456789abcdef";
        bytes memory result = new bytes(42);
        result[0] = '0';
        result[1] = 'x';
        for (uint i = 0; i < 20; i++) {
          result[i * 2 + 2] = hexAlphabet[uint8(addrBytes[i] >> 4)];
          result[i * 2 + 3] = hexAlphabet[uint8(addrBytes[i] & 0x0f)];
        }
        return string(result);
      }
    }
    
    // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title IERC165
     * @dev https://eips.ethereum.org/EIPS/eip-165
     */
    interface IERC165 {
        /**
         * @notice Query if a contract implements an interface
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @dev Interface identification is specified in ERC-165. This function
         * uses less than 30,000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic interface
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    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);
    
        function balanceOf(address owner) public view returns (uint256 balance);
        function ownerOf(uint256 tokenId) public view returns (address owner);
    
        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 transferFrom(address from, address to, uint256 tokenId) public;
        function safeTransferFrom(address from, address to, uint256 tokenId) public;
    
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @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 `safeTransfer`. 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);
    }
    
    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title SafeMath
     * @dev Unsigned math operations with safety checks that revert on error
     */
    library SafeMath {
        /**
         * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b);
    
            return c;
        }
    
        /**
         * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Adds two unsigned integers, reverts on overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a);
    
            return c;
        }
    
        /**
         * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
         * reverts when dividing by zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0);
            return a % b;
        }
    }
    
    // File: openzeppelin-solidity/contracts/utils/Address.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * Utility library of inline functions on addresses
     */
    library Address {
        /**
         * Returns whether the target address is a contract
         * @dev This function will return false if invoked during the constructor of a contract,
         * as the code is not actually created until after the constructor finishes.
         * @param account address of the account to check
         * @return whether the target address is a contract
         */
        function isContract(address account) internal view returns (bool) {
            uint256 size;
            // XXX Currently there is no better way to check if there is a contract in an address
            // than to check the size of the code at that address.
            // See https://ethereum.stackexchange.com/a/14016/36603
            // for more details about how this works.
            // TODO Check this again before the Serenity release, because all addresses will be
            // contracts then.
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
    }
    
    // File: openzeppelin-solidity/contracts/drafts/Counters.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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 {
            counter._value += 1;
        }
    
        function decrement(Counter storage counter) internal {
            counter._value = counter._value.sub(1);
        }
    }
    
    // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @title ERC165
     * @author Matt Condon (@shrugs)
     * @dev Implements ERC165 using a lookup table.
     */
    contract ERC165 is IERC165 {
        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
        /*
         * 0x01ffc9a7 ===
         *     bytes4(keccak256('supportsInterface(bytes4)'))
         */
    
        /**
         * @dev a mapping of interface id to whether or not it's supported
         */
        mapping(bytes4 => bool) private _supportedInterfaces;
    
        /**
         * @dev A contract implementing SupportsInterfaceWithLookup
         * implement ERC165 itself
         */
        constructor () internal {
            _registerInterface(_INTERFACE_ID_ERC165);
        }
    
        /**
         * @dev implement supportsInterface(bytes4) using a lookup table
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool) {
            return _supportedInterfaces[interfaceId];
        }
    
        /**
         * @dev internal method for registering an interface
         */
        function _registerInterface(bytes4 interfaceId) internal {
            require(interfaceId != 0xffffffff);
            _supportedInterfaces[interfaceId] = true;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic implementation
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721 is 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 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
        /*
         * 0x80ac58cd ===
         *     bytes4(keccak256('balanceOf(address)')) ^
         *     bytes4(keccak256('ownerOf(uint256)')) ^
         *     bytes4(keccak256('approve(address,uint256)')) ^
         *     bytes4(keccak256('getApproved(uint256)')) ^
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
         *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
         *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
         */
    
        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));
            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));
            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);
            require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
    
            _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));
            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 != msg.sender);
            _operatorApprovals[msg.sender][to] = approved;
            emit ApprovalForAll(msg.sender, 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 {
            require(_isApprovedOrOwner(msg.sender, tokenId));
    
            _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 `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 `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) public {
            transferFrom(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data));
        }
    
        /**
         * @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) {
            address owner = ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
    
        /**
         * @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));
            require(!_exists(tokenId));
    
            _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(uint256) 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);
    
            _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);
            require(to != address(0));
    
            _clearApproval(tokenId);
    
            _ownedTokensCount[from].decrement();
            _ownedTokensCount[to].increment();
    
            _tokenOwner[tokenId] = to;
    
            emit Transfer(from, to, tokenId);
        }
    
        /**
         * @dev Internal function to invoke `onERC721Received` on a target address
         * The call is not executed if the target address is not a contract
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
            internal returns (bool)
        {
            if (!to.isContract()) {
                return true;
            }
    
            bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, 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);
            }
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Enumerable.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Enumerable.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    /**
     * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721Enumerable is 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 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
        /*
         * 0x780e9d63 ===
         *     bytes4(keccak256('totalSupply()')) ^
         *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
         *     bytes4(keccak256('tokenByIndex(uint256)'))
         */
    
        /**
         * @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));
            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());
            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 _burn(uint256) 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;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Metadata.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
        // Token name
        string private _name;
    
        // Token symbol
        string private _symbol;
    
        // Optional mapping for token URIs
        mapping(uint256 => string) private _tokenURIs;
    
        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
        /*
         * 0x5b5e139f ===
         *     bytes4(keccak256('name()')) ^
         *     bytes4(keccak256('symbol()')) ^
         *     bytes4(keccak256('tokenURI(uint256)'))
         */
    
        /**
         * @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));
            return _tokenURIs[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));
            _tokenURIs[tokenId] = uri;
        }
    
        /**
         * @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];
            }
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    /**
     * @title Full ERC721 Token
     * This implementation includes all the required and some optional functionality of the ERC721 standard
     * Moreover, it includes approve all functionality using operator terminology
     * @dev 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
        }
    }
    
    // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor () internal {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        /**
         * @return the address of the owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(isOwner());
            _;
        }
    
        /**
         * @return true if `msg.sender` is the owner of the contract.
         */
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         * @notice Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address newOwner) internal {
            require(newOwner != address(0));
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    // File: contracts/TradeableERC721Token.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    contract OwnableDelegateProxy { }
    
    contract ProxyRegistry {
        mapping(address => OwnableDelegateProxy) public proxies;
    }
    
    /**
     * @title TradeableERC721Token
     * TradeableERC721Token - ERC721 contract that whitelists a trading address, and has minting functionality.
     */
    contract TradeableERC721Token is ERC721Full, Ownable {
      using Strings for string;
    
      address proxyRegistryAddress;
      uint256 private _currentTokenId = 0;
    
      constructor(string memory _name, string memory _symbol, address _proxyRegistryAddress) ERC721Full(_name, _symbol) public {
        proxyRegistryAddress = _proxyRegistryAddress;
      }
    
      /**
        * @dev Mints a token to an address with a tokenURI.
        * @param _to address of the future owner of the token
        */
      function mintTo(address _to) public onlyOwner {
        uint256 newTokenId = _getNextTokenId();
        _mint(_to, newTokenId);
        _incrementTokenId();
      }
    
      /**
        * @dev calculates the next token ID based on value of _currentTokenId 
        * @return uint256 for the next token ID
        */
      function _getNextTokenId() private view returns (uint256) {
        return _currentTokenId.add(1);
      }
    
      /**
        * @dev increments the value of _currentTokenId 
        */
      function _incrementTokenId() private  {
        _currentTokenId++;
      }
    
      function baseTokenURI() public view returns (string memory) {
        return "";
      }
    
      function tokenURI(uint256 _tokenId) external view returns (string memory) {
        return Strings.strConcat(
            baseTokenURI(),
            Strings.uint2str(_tokenId)
        );
      }
    
      /**
       * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
       */
      function isApprovedForAll(
        address owner,
        address operator
      )
        public
        view
        returns (bool)
      {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
        if (address(proxyRegistry.proxies(owner)) == operator) {
            return true;
        }
    
        return super.isApprovedForAll(owner, operator);
      }
    }
    
    // File: contracts/OpenSeaAsset.sol
    
    pragma solidity ^0.5.2;
    
    
    
    /**
     * @title OpenSea Asset
     * OpenSea Asset - A contract for easily creating custom assets on OpenSea. 
     */
    contract OpenSeaAsset is TradeableERC721Token {
      string private _baseTokenURI;
    
      constructor(
        string memory _name,
        string memory _symbol,
        address _proxyRegistryAddress,
        string memory baseURI
      ) TradeableERC721Token(_name, _symbol, _proxyRegistryAddress) public {
        _baseTokenURI = Strings.strConcat(baseURI, Strings.fromAddress(address(this)), "/");
      }
    
      function openSeaVersion() public pure returns (string memory) {
        return "1.2.0";
      }
    
      function baseTokenURI() public view returns (string memory) {
        return _baseTokenURI;
      }
    
      function setBaseTokenURI(string memory uri) public onlyOwner {
        _baseTokenURI = uri;
      }
    }
    
    // File: contracts/OpenSeaCollection.sol
    
    pragma solidity ^0.5.2;
    
    
    
    /**
     * @title OpenSea Collection
     * OpenSea Collection - A contract for easily creating custom collections (asset contracts) for OpenSea. 
     */
    contract OpenSeaCollection is Ownable {
      address proxyRegistryAddress;
      string public baseURI = "https://api.opensea.io/api/v1/metadata/";
    
      constructor(address _proxyRegistryAddress) public {
        proxyRegistryAddress = _proxyRegistryAddress;
      }
    
      function openSeaVersion() public pure returns (string memory) {
        return "1.2.0";
      }
    
      function setBaseURI(string memory _baseURI) public onlyOwner {
        baseURI = _baseURI;
      }
    
      function make(string memory name, string memory symbol) public returns (address) {
        OpenSeaAsset asset = new OpenSeaAsset(name, symbol, proxyRegistryAddress, baseURI);
        asset.transferOwnership(msg.sender);
        return address(asset);
      }
    }
    
    // File: contracts/Migrations.sol
    
    pragma solidity >=0.4.21 <0.6.0;
    
    contract Migrations {
      address public owner;
      uint public last_completed_migration;
    
      constructor() public {
        owner = msg.sender;
      }
    
      modifier restricted() {
        if (msg.sender == owner) _;
      }
    
      function setCompleted(uint completed) public restricted {
        last_completed_migration = completed;
      }
    
      function upgrade(address new_address) public restricted {
        Migrations upgraded = Migrations(new_address);
        upgraded.setCompleted(last_completed_migration);
      }
    }

    File 2 of 2: OpenSeaAsset
    // File: contracts/Strings.sol
    
    pragma solidity ^0.5.2;
    
    library Strings {
      // via https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
      function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        for (uint i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (uint i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (uint i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (uint i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        return string(babcde);
      }
    
      function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory) {
        return strConcat(_a, _b, _c, _d, "");
      }
    
      function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory) {
        return strConcat(_a, _b, _c, "", "");
      }
    
      function strConcat(string memory _a, string memory _b) internal pure returns (string memory) {
        return strConcat(_a, _b, "", "", "");
      }
    
      function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
          return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
          len++;
          j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (_i != 0) {
          bstr[k--] = byte(uint8(48 + _i % 10));
          _i /= 10;
        }
        return string(bstr);
      }
    
      function fromAddress(address addr) internal pure returns(string memory) {
        bytes20 addrBytes = bytes20(addr);
        bytes16 hexAlphabet = "0123456789abcdef";
        bytes memory result = new bytes(42);
        result[0] = '0';
        result[1] = 'x';
        for (uint i = 0; i < 20; i++) {
          result[i * 2 + 2] = hexAlphabet[uint8(addrBytes[i] >> 4)];
          result[i * 2 + 3] = hexAlphabet[uint8(addrBytes[i] & 0x0f)];
        }
        return string(result);
      }
    }
    
    // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title IERC165
     * @dev https://eips.ethereum.org/EIPS/eip-165
     */
    interface IERC165 {
        /**
         * @notice Query if a contract implements an interface
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @dev Interface identification is specified in ERC-165. This function
         * uses less than 30,000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic interface
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    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);
    
        function balanceOf(address owner) public view returns (uint256 balance);
        function ownerOf(uint256 tokenId) public view returns (address owner);
    
        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 transferFrom(address from, address to, uint256 tokenId) public;
        function safeTransferFrom(address from, address to, uint256 tokenId) public;
    
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @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 `safeTransfer`. 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);
    }
    
    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title SafeMath
     * @dev Unsigned math operations with safety checks that revert on error
     */
    library SafeMath {
        /**
         * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b);
    
            return c;
        }
    
        /**
         * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Adds two unsigned integers, reverts on overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a);
    
            return c;
        }
    
        /**
         * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
         * reverts when dividing by zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0);
            return a % b;
        }
    }
    
    // File: openzeppelin-solidity/contracts/utils/Address.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * Utility library of inline functions on addresses
     */
    library Address {
        /**
         * Returns whether the target address is a contract
         * @dev This function will return false if invoked during the constructor of a contract,
         * as the code is not actually created until after the constructor finishes.
         * @param account address of the account to check
         * @return whether the target address is a contract
         */
        function isContract(address account) internal view returns (bool) {
            uint256 size;
            // XXX Currently there is no better way to check if there is a contract in an address
            // than to check the size of the code at that address.
            // See https://ethereum.stackexchange.com/a/14016/36603
            // for more details about how this works.
            // TODO Check this again before the Serenity release, because all addresses will be
            // contracts then.
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
    }
    
    // File: openzeppelin-solidity/contracts/drafts/Counters.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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 {
            counter._value += 1;
        }
    
        function decrement(Counter storage counter) internal {
            counter._value = counter._value.sub(1);
        }
    }
    
    // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @title ERC165
     * @author Matt Condon (@shrugs)
     * @dev Implements ERC165 using a lookup table.
     */
    contract ERC165 is IERC165 {
        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
        /*
         * 0x01ffc9a7 ===
         *     bytes4(keccak256('supportsInterface(bytes4)'))
         */
    
        /**
         * @dev a mapping of interface id to whether or not it's supported
         */
        mapping(bytes4 => bool) private _supportedInterfaces;
    
        /**
         * @dev A contract implementing SupportsInterfaceWithLookup
         * implement ERC165 itself
         */
        constructor () internal {
            _registerInterface(_INTERFACE_ID_ERC165);
        }
    
        /**
         * @dev implement supportsInterface(bytes4) using a lookup table
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool) {
            return _supportedInterfaces[interfaceId];
        }
    
        /**
         * @dev internal method for registering an interface
         */
        function _registerInterface(bytes4 interfaceId) internal {
            require(interfaceId != 0xffffffff);
            _supportedInterfaces[interfaceId] = true;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic implementation
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721 is 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 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
        /*
         * 0x80ac58cd ===
         *     bytes4(keccak256('balanceOf(address)')) ^
         *     bytes4(keccak256('ownerOf(uint256)')) ^
         *     bytes4(keccak256('approve(address,uint256)')) ^
         *     bytes4(keccak256('getApproved(uint256)')) ^
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
         *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
         *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
         */
    
        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));
            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));
            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);
            require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
    
            _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));
            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 != msg.sender);
            _operatorApprovals[msg.sender][to] = approved;
            emit ApprovalForAll(msg.sender, 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 {
            require(_isApprovedOrOwner(msg.sender, tokenId));
    
            _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 `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 `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) public {
            transferFrom(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data));
        }
    
        /**
         * @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) {
            address owner = ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
    
        /**
         * @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));
            require(!_exists(tokenId));
    
            _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(uint256) 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);
    
            _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);
            require(to != address(0));
    
            _clearApproval(tokenId);
    
            _ownedTokensCount[from].decrement();
            _ownedTokensCount[to].increment();
    
            _tokenOwner[tokenId] = to;
    
            emit Transfer(from, to, tokenId);
        }
    
        /**
         * @dev Internal function to invoke `onERC721Received` on a target address
         * The call is not executed if the target address is not a contract
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
            internal returns (bool)
        {
            if (!to.isContract()) {
                return true;
            }
    
            bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, 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);
            }
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Enumerable.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Enumerable.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    /**
     * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721Enumerable is 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 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
        /*
         * 0x780e9d63 ===
         *     bytes4(keccak256('totalSupply()')) ^
         *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
         *     bytes4(keccak256('tokenByIndex(uint256)'))
         */
    
        /**
         * @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));
            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());
            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 _burn(uint256) 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;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Metadata.sol
    
    pragma solidity ^0.5.2;
    
    
    /**
     * @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);
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
        // Token name
        string private _name;
    
        // Token symbol
        string private _symbol;
    
        // Optional mapping for token URIs
        mapping(uint256 => string) private _tokenURIs;
    
        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
        /*
         * 0x5b5e139f ===
         *     bytes4(keccak256('name()')) ^
         *     bytes4(keccak256('symbol()')) ^
         *     bytes4(keccak256('tokenURI(uint256)'))
         */
    
        /**
         * @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));
            return _tokenURIs[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));
            _tokenURIs[tokenId] = uri;
        }
    
        /**
         * @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];
            }
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    /**
     * @title Full ERC721 Token
     * This implementation includes all the required and some optional functionality of the ERC721 standard
     * Moreover, it includes approve all functionality using operator terminology
     * @dev 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
        }
    }
    
    // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor () internal {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        /**
         * @return the address of the owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(isOwner());
            _;
        }
    
        /**
         * @return true if `msg.sender` is the owner of the contract.
         */
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         * @notice Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address newOwner) internal {
            require(newOwner != address(0));
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    // File: contracts/TradeableERC721Token.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    contract OwnableDelegateProxy { }
    
    contract ProxyRegistry {
        mapping(address => OwnableDelegateProxy) public proxies;
    }
    
    /**
     * @title TradeableERC721Token
     * TradeableERC721Token - ERC721 contract that whitelists a trading address, and has minting functionality.
     */
    contract TradeableERC721Token is ERC721Full, Ownable {
      using Strings for string;
    
      address proxyRegistryAddress;
      uint256 private _currentTokenId = 0;
    
      constructor(string memory _name, string memory _symbol, address _proxyRegistryAddress) ERC721Full(_name, _symbol) public {
        proxyRegistryAddress = _proxyRegistryAddress;
      }
    
      /**
        * @dev Mints a token to an address with a tokenURI.
        * @param _to address of the future owner of the token
        */
      function mintTo(address _to) public onlyOwner {
        uint256 newTokenId = _getNextTokenId();
        _mint(_to, newTokenId);
        _incrementTokenId();
      }
    
      /**
        * @dev calculates the next token ID based on value of _currentTokenId 
        * @return uint256 for the next token ID
        */
      function _getNextTokenId() private view returns (uint256) {
        return _currentTokenId.add(1);
      }
    
      /**
        * @dev increments the value of _currentTokenId 
        */
      function _incrementTokenId() private  {
        _currentTokenId++;
      }
    
      function baseTokenURI() public view returns (string memory) {
        return "";
      }
    
      function tokenURI(uint256 _tokenId) external view returns (string memory) {
        return Strings.strConcat(
            baseTokenURI(),
            Strings.uint2str(_tokenId)
        );
      }
    
      /**
       * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
       */
      function isApprovedForAll(
        address owner,
        address operator
      )
        public
        view
        returns (bool)
      {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
        if (address(proxyRegistry.proxies(owner)) == operator) {
            return true;
        }
    
        return super.isApprovedForAll(owner, operator);
      }
    }
    
    // File: contracts/OpenSeaAsset.sol
    
    pragma solidity ^0.5.2;
    
    
    
    /**
     * @title OpenSea Asset
     * OpenSea Asset - A contract for easily creating custom assets on OpenSea. 
     */
    contract OpenSeaAsset is TradeableERC721Token {
      string private _baseTokenURI;
    
      constructor(
        string memory _name,
        string memory _symbol,
        address _proxyRegistryAddress,
        string memory baseURI
      ) TradeableERC721Token(_name, _symbol, _proxyRegistryAddress) public {
        _baseTokenURI = Strings.strConcat(baseURI, Strings.fromAddress(address(this)), "/");
      }
    
      function openSeaVersion() public pure returns (string memory) {
        return "1.2.0";
      }
    
      function baseTokenURI() public view returns (string memory) {
        return _baseTokenURI;
      }
    
      function setBaseTokenURI(string memory uri) public onlyOwner {
        _baseTokenURI = uri;
      }
    }