ETH Price: $1,818.73 (+0.39%)

Transaction Decoder

Block:
13846545 at Dec-21-2021 04:57:42 AM +UTC
Transaction Fee:
0.255414082 ETH $464.53
Gas Used:
3,317,066 Gas / 77 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
0x3E8aE53D...64007E744
0.5 Eth
Nonce: 0
0.244585918 Eth
Nonce: 1
0.255414082
0x8981c60f...F9cF68f62
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 4965745849516225432520124764727501345466695076190518392613553452591458747206154384890590382899774284024890510014226998279664019488601491931544557570680149892918957330049613727238194268686443206711083184715987135693929335045325913153468979238330593720853578535996932496318507617670750404942377775794657511006612817449113294567269624755667737545176129650001829042324927490237213639028229199118581585361101175896891007564942985947254440854064317748312990272059301837782132503052245734259767053338435487304054930212129054172561855213687780636606937449963897518823674757843694019281105709029178358405235441217876149626946653355864916354455991754557130654045256488944509850342346330330313552324759267420324229804029454135145094452085962889243675090228397310213754792369625300303570024423831219496367164767852255677672454319015502709920014479323977096153281216763641627213510069726673919595150780035356903896097224852889211401422355856012582473784134490705886624334083910449053912119093959445722891985633335763012854618617434947742856579611193421562877812822972606113504606356957953573548502105975581789342930231491431026127129682586347368355439073342616682551438667185190004998605078742664663513338422944460505193928371681821130414449534926042297157014629402732759398361636078883877182370401460001580735628171107801856108812667612198470774879912454553174819768115019578737376368926540390455915898594975941505484309257779579326046756020609357936376337286204938540013294214709969783145998565144221985673056471158234261717697175925820900471547868939174743229254343916956653688053076087609381836071445426303923142488239446769896239836580785052736590264797949175790419053915286300310146269036565710895937551673994758071985739880279678209521526281372906168618030806253751169952625639255239750116279101378249324220946049569963605670817293149008267058334347066363484524996277802412946186847097883702732364960374671609736925264247881590127101128973396062191841807609867593919734488807840193216106122146113260693109629216663371211887086155921622947815039258397543784081960541682412100479713871375440169631215959406508450700025598350601277818517060585565293678069818458503659047621026772179689849481553369990434713865806961400830034766642041929012487666162574592944665317964984830718865155848023355527362040089933431665605966444914523281105289368771732581617727282672250287165617587904282090710331212848644429232213408200908801831046843648387683785660265632260622241355533370672968808838150271625501205618534215485588947055308639090024410661796840164366298272067065631023971429742527390441184514358075655739086018188756139843968366342001619355481509077756264203319096970922523936201384589740723379319761922807784778476069720872185263394657208765034160511813861534313311413565798484932043552026346843014398101627749854655712989447350323652999700827554150715649964493880144490491988814616253729700054912133825317718121243314276289865381392789825994057685785713321437173183817735286193315181475232284522346019873656747005368846935083179734519227073812148110949338343612605452755944152219030116520407912585754446905792288165761009248648938207035741762842659237757292770117009204512997372840950397909529395738922255537894384410155817102778311679054624722497949388767106656418175311920016446640113859725033604777889890152875952062236132001737522501786617856212589185240299352964764911927240350023135151121080988363914644107883339353524915346293623820893407613517450168706121623665738774392711819739153277445690032234183657150294629813784849900713308005278612698826734857189017912532300208281364869318442359618850417529153899702745067782434633994815156129734041586403445906528218174410131142354862537850574911902913634727966606815541694625887445885924803445717717616885184124567655579892592694270183085248838051297000178408476611818605795000958308817418962035954449647806353355320884701765473513405308018441412690947120662954522469818464378159493942563721648277225202848696035311778179018116716976570302661580222413889570428883971743751617952471143107994960496140979096278454589399827681331743213834457380717779537989334690904913423130206942432985322625701173822585343830580451174859118199029330365395748196388399543537755335355762738748706414254422040000304426743457011399988882252524665266142318455277856692798100108856921521636962644923550423309325617239250410400005954178061206890307393379414075320949288793286748283870276508859606532966765830854542573673929864959107357923187011091466355809225338684452860051458508103503726081705393055667326697845560844496403490408360603473162092296685912779812123437573212668824708250265513909506538858011423689592525992229934113106535707107701592445626936218313223945254620753031435352597415069892148592638129393261980245014866083453658901310718919137442950633643292080538099956849835365093874233807980311467519840983687818404052778680465281585562772155870182760792941761093889299888948919858915458713166233961251569779754888657919803738363409332466843924884152177642406298610697322971930368305020906729062314752974721243333051337700534864920046330642671906278167592574689629227085269156589915918168049089651807799921381301142154877120943745554401839168509080970234250805779600156292724701539926103827625588095805198745445222528667280349441184791873158624876729371076001870702786010849708151334931966116768741013116218082193019157331834702560543056605247565199422741843682325550147399300426701282634127054502460516954613565385867806447144544776918022437346660904723423611595332842554151996019849300671726850529135630365767023164055576411573534767543135910369254563586585943305679700007512721642792250805021717551819540575974019840682947030555674004117745003306946614535132359570206723986020114924036163342969537969101572132321707487971806467423831721596311076216026911710965202183663490977829165405040225057952830193092658857905217650825705075576096509292471269787087154527581439536678843640946264080096331269271088519730817298330848890540912053822552469343449711678572156497626612588779790427663839827968448610279870307744147691378894436753099828688316349353806650007150655643898849599955173024333338398537656576336983952274511179629443538343832015395197984212982539208809357102064700311968314578962759533778275428590035948925997751611126500930200839807917307257434103657472198983782006800722715240140326983621446992484748793211087930052650279634267382781589428543331567721693407407454972163116540933171586400605098433534443971904166670635681118417992668175975290722257962535852905854454915266189429607055782793866315678768193533288869799768524526361885183774914871056105358142024247501659518714600059018604291734944774208376060090670727351008906428572650670535686055783086536554442207688427656058462822488873032905394330902268159922897458735687929164652962084160442391738530390302096147785925701553778714349231026588346090791648240813703635601007808418427534991748154050040902040554792358932072705217602165534200403591944688961969459252137825334544570777564347204812191931054313091099977726168459149862137312308965753241927468162585890258724517448834423141938296209603192015317715315313358464978301923814985126656839421062228489428057594617064093879510572289488802518834268867543415061789537110601105977590758279915666264149275029806122926658025809816142936211301981648237148521382142805085888574064281561464406129399637984934322060748819965234697343635552355697392936347459982588832250289579586989438559414032983810276991718376463164719963942820640166882155712339792875483310114096969164222365128985685048103936659663415457693295115212582289132259130852495297803561021289159620598046030836852113082504457481313216687600523960186276489842440539950989217171664850351221782041347373584988325639798144334530033520935947162151045389040569240235202831133391308529160402962454526870096719720151125873225694405961122830106377241576394597290022903619396274181171756021707449569876030182374751671664439442837545956509629206487853642565865303279877330036935251190038505665322256927203903337042393626872279790584992389669044531569015580932211134762259540244265145850266388576433653461726013971272451778876044902510813144466796043747668895165890407655183369693421791636905052395615141376363133125838575356821418346054902034202646040751269757232675590484368143894191485443622759600560374512873087811892640883027119102223744618558621087866173385560940617052001779288533155582352339309504456262418766075260886142540615796551184375776576975426438337920476032945459819044240346393119860007816908500729281129178906985684980528568200020236122513128391449672048966936463364961392828585047493664622471443236534523257951922348045231252207285554318124041996859456140478948901851625634089277826543003180866782627280336534918790046383970145275184326929798972042437043451622866284161165039917693739027186004174637432236357175735493461362605395420369796701791194849856692335094625099298469044895919489587361983125429545167149709631531958318417577696318252862955092863110761577889662307729793652111110116440206348498104621858568230299209302791776263177197635677499281743697953562252104345470087133675613234125989034510627550937753071872651139627031187432944788479408869818884287091743345191664272485836165857538678538453052954281955417165693905693745969250699606482172504351747756442668057749467044566353824838823142404271311769939510928459041927412484426704513239494808833304824792652589730972475763238161869794738580095520886251473034934234636510403769180197865662108698017146830286083878383070144957165619108289618033686156318200261284923837281655806082110584885912052089342888872092274286842734306802722129306782735129645375988146573531810399338250706374783301132530411339818426859280557022378219017748413965063373305314439939384252016055022923554746431301570001614403421771516024039023458442367934573186601815982831618829720729771907929215842175024568300455208073317510886036843883285269307277736476074587627124838458851783771796358098463047769814027190643179068416690971376571184551232997899642075971493045714838850335950893372831530683876858692588658808946422082020398402424726955748985643980056027969376766801154863075632263255591689002929780301133784750935434942630766296601951585512053511546534240890643600789770690442385979113070818301161306099029346397072301675117881311210794754954024180800718096375736526516790533925776189796078178569701193437031954483884713286331542007905011717401631236058033720539574086593177103599359480895384461843112478114595437075865376443543247438969859262006385447094856794398411177181745941693910988249908727290986495428404922320345303830883422433482383114435453201353589798941676664419471980655874607751167078532328448547997903664619126839307704912848456662220676881280886370365210403771129908223083705666334684610557383211774093420463538206144222096384896891330930851650515864889152798898034517014757146163357974131975391579252153654642486704998889089698128316937659950468046049067179401692051934022640682564583320255715103870185526489369336962693589489479065306562731584366320621592790879663053424488411635911962790542288071990885968971054633066230200010328412085543043945049674837928008304722752988392844382007153383563546918747378719213530189548397742278849367091862823280339755844259386299312127257800178079114901611468039102927746929609965611404599184804864554082689118300675418800605575395410346483799197808335126288022671008180036363052472261220698222172488287170255171719890402694809224761328489446825937851122479923106287987224135895701980579957836839253140215780975033186767748749887127744168143144371199315329677082305332271919538273530349603507366641511524753863999584603679779350007894650069629907245924612912590763441345765747878240816659442035706306758842446914256259434544794732852201805831759026242244673505712960652627328926411751387221757153565973640901469538574007812166676963279119466759038585607696045304585097789218902783443154124886462913253798898512643750834017882761886717841144441541513917233445272480353675935382584884120860865569444986186517899964172995063484579936572159553180261645056536573344201289983001129689071684077148409098596082291635364350035259029234107278919027898090824470325663668203908885646569446583264412305893767296434036475028530850503203315258098564254203079846189268302854422717543655792936418835688057060518707956335737191278980675938450941408679327625316311932811001721147174024793887274347630755330776373802853103582354435627632872780080861870844521015982911793900229132244915694802654321536624582976945655553486120352992877511358943626380163046438338350438332607614731930801157215766965234965570348055170085974273129804259440933141417327632259640503229203627642994043041842074733571964782657180455323182964106960781580990040322320990303220824237695838536836433143618716417063431434549797053323078989241438830128607805915670070791536388004509277379660340611577734227062807703619405784260703958106320617285060344325200354838911565976873157599354316228600352100112221383073770706374137646407721414852361488599554930711620251012733064867552385236136759267125051733552662524558750467807538453120189498259168868010406435265821828836264344161113488143032611858958598067368762872449506334805887781475128042949794737243211644184731157658730937726791368162010090411678027825889649182071206661958810894129498843464072046704565175786876111411238393582176837788993241717748483067124288048139417590335074807940972067676424733230624030169287651994699469630822719388600688948233095382609908590529239625832482316368085915324307207907239466018606387295519808829463904854308648925586983519516731687363993677256717533951228428816347605231508737208517574682478610334296397071331716618924276507394014561785229209714633152264587875863617171479997150286670993633795516560751691267442236666941414237868457301022957176045903572799029336894516889563260750183655364518015629654466240326589692838502430519820109787878846772838568630424356546631706118460722540551513325751501320764427430410247030916066181680224112329549225234261786200990062939097229276623004422045469448288282600918948547669028725022802828178769242114417772083593145081858894032820419479815288955894695422710380925637602039222666010811478508915679365566190355618256717200356155252437712948188449793483137735779782878764853274418986951064537479296519496862176568008302031182721234626493416372766649965649548671935853028357781113350701530565151164639443700361510983372304762809251558350608885102529309521963760462552497475055581312607068024356417099877370371314392185142161135728841197758088840327828213966723141331950960760862733988807210850772435140717268787437543724792174664363641433494547240560216898170802519276094741022335743376053642461907687427695126119927917360731999201615442235278759523166726340478512364528109471340081652626466368934634785454677293811075070425307441421580258400719363083935145379218834192308780445227549340339973776230765844315198321885859558238826848995075919802097694493757381435795348741364912130788795468449707691483112344957032144459045085321841388701476049322530015659469538671173858272534090639883229390106114920179189422846400864457537585726114066830312820653722960247890015817138672526170889743144688800774435716656946439359931177181168106307494155507308262672408552377066633087401649815754058569325222739868115330295701535390801430372463674773355003894543756920335537271096919863607920268215546813984738973067583090103183641262951854326738450920022999272742310732290072033202935477801613721442155736586267815339089931415519002244321069485753478684929143149828646895533262061778983086606192586595527664040190477669855827696794792300327231516655768741413911075559353083937163044100922243682513660189446172015553356561134172060507054783373753970334267956758759739195647678069968562019486955767917336865507695764325301426009549710293624141554278848628021977613971299680639929833066830438149898846741244826914672138942543320709153669036799291488048584079963144879308534106507508392528174094691051662111454322569003687508829299592935373429251320726545600643724981017878234774960862461678018277073943677262297692114053484377910253605827491857101341881818854014792670121661073835019327129668144985498638136247610988264409473181366464902797509663423740457949404881975439930601891503879878893162042226734265017520883136413636730564780892725106348784930117359666565033478464774796672947956620680829180667687686417147746974815621165665432095925331196252277772491998639427838647969306638320007678066318079352815685443742634717453898151644828750955465956101737235345590217825404604567985761992902837127063764156988544063074836182932303792823879525642968551438644245856499503967156107314114497679092807963886420251371058831676445817006477495878360970489524596056246710957418572251744064857738609757610224279381454223303389569124865729560276691038217276979983061311554745587425974158928974386133396514692075967947748904518116715873760969241518453280170150897311247681407857661218807635244748425785574000826839413123752084021309879676459444379713461543137571903821812024863065790709897125026337200511571653608527693636053075517669495134724912362609506644787720042791223611145422192762888970539457995151370199171996092896687844597772699836145193695035864247625084420726287979624795963265728704269852552015628665919840440416913376374404422464296793265361317118529773990787969639267040017544671922399273805210704328092229552985848495409222079419353543417980291274382312204896700388491266441605701013923496387681561332634173609761990442477091437905694284692431400731594155173615786036221910474417317648929906984699231868690933596890741358972222131216730754685658410759267280028454433084293965949541187802161693270044482912668455416097075995697055998793661195046995220324347793618538055726911653337342270600906088921658476459994918976362859253484906861491437897898147443923383644834559172818580254275691154667346632888085566261669290751614224861549804960575679433119812322114885745575137989637175254167826323670056452791694398700268412636817657549817972487248223112499102580144717857192042670353236865622796378838713435135363573241210219938282721806908621689106537427074734099821758728084536281160517919204103510748414495462451442848883185076107916686303045896990299540409007614974041231216618947970015936984255539223216123294603359936683123487343490680298023635310969959625211026567412424862164344078029995393966290579956727206627152694151171981608884729837676056276478954258941581965073228067992520136655690703160156932830659595686479122773859220439009640356984705815678523814392520408875988856583592578757466607265696329058370030642768370860898674605805813879987424247473827665358233488507698318919056084018774702240447911981540953521110397268977491386998473567884685799899302442829407408899849410077902783296424477459483689343798176950354669344012973108365783413341431386113332075172877422911349925939528191548602882803854426278336787212855276844790954812869248573758869387932848590862266293355198362606245190419690055253138787844590881009387310810584521318709389243508367078443727067488444448169971766681294639394267832184551591791250962459858198459114598530291834005724537018297958449335233598385775091384456421103915464121678661905451139529250957658516684112809847257232704067604969610291183735596302156681524233882159296190733157108185871430976303557414400240165991288358364770129338464295280993705827348733278176600229841848518429430933779019872858175818297295831697997619115558821722198682362852458815629028407743637386450061193355629144830666788137833461002495689959958628234346641470143041806991115945161526135663653858937319761968756705821262014582755517689782480308212468711151571521771588917076110487311099104671930353175946166869574293087917749699239866328687235905919607878643881370056969358044344423351825814678557510099575825184296160793063819487091657717687180831991771190344259532504167831556633943149719054109499701151489087090562991923338714625984460186457418827412836841903566861502654346869198507923329458513302591113860357125135905675694930989340680538440075171585103193830139271915636458480511015429915784997780088285832472772841619651744493182398795654173669311211351303137476171071793628361806201118472443572070150913077708152083102213806389317318056175194969874436490373563193849489669502142811375404205757626434468303028373483931144408309274379694086082957474684125697537461037968580109887608821991645639531947315435256258007151282827998368610373603051570627647984182786707910372168560842896352722761961121282141477421485196023511471780271670615453324844920542247506794584782687548973415961777309577994508448309003429112178450367332594188710882108458077268507214720641725536760021300916721068685442545202325831245079043355653047171692603829925047825068602607370313310875598168606339871835873280765344681741565254972864848279370828689758140982650368140606420589471810526413387913119990299046423773451274589457996055033046900444796257718216410313678841866139524996764132691180784869702661584581648291661644466076297659734637950454254008168625031414283316385002017144978756382737034798765765258520945096580758826323494960907480604366486951296592584060812932883625271421017129526106826632527882889454418003223082897639647942880917075085913641057625636252671193104486445201874691768696891963641359488725456721370817993223267877577048766341925041625645313456022102965990253522105891026391425688730409449026195129967935537833175137101603183080799561544823498008757802170650449117372418266471739468192663153730194135115863910038501647445488100857015148696295458672346604260809459490308274069210327123616295028085009348752862688053944431948748326034879022817832275328123362992043922909918327892782766566372799718808956418288796561602366321115529459798344481151021908826100333044645423317068758207543650635861168479496748154438805233671150779582714898476884580786410420260661677818941201508165134926501871568582121473345105057872753973979553392243635722593431958439911147554760451452500621655475866704858444015670937959182904601764407711140320384217593505119457748750394291799097160145552953940507831128627548170905827661519929820718845154497870327284802701163968867218528654206838320653674803124870842007843424433931839439935331002481218467538682774363947357535120132927704853943575994565619127871632726401128645000684798588468172652973613495830790630575638800593010302706066640939073879254082651037332968018620551137692988603544319572775887060616341269416693697965212594376116966409323091753080408901994972766228863295245261454079163498848407736179198550392618341532618080787342149588735324977487020802914405195301827968436984715241211509572438357829566369171128045841397414453021177597216500091926198265691660142638600129951280205682041888833080543035690308061567598680109140155866207521864887397525786208550134041225629161402117160176732601980550586560163309047305974126587217975000187284208227374739089528201972687278135736816749851095815558819408741202000261992542179281480428661319618955040776110403520319940508433224433317932740967894588455337157978619061265375896490052895704051870384340580727941427924608248019631960430078336066470866168262943575867146682610294741488538708696256824640490758237975670266971030170654258637992949673476231322335219735732602121293263621352367449178892673279706702675345428516220504152890170957767515923994428919089957597526986425488081614552220903417363182523336806234300393873378153516788116933905637867504887652281681975189007176727809725219311119183613652800938307261390138260899668052761845918483707797251302981937525192850582405741048159788703194433980358839492295854259296827538309163937350975449527539082647868042276025945554423998652976624466758569105417283270781802716002908556221994847290808537914136836790849747151713533233175213529030922296288050358988706831159639101203156471911625686582823917338629857731595177884115713963774728565121349125755236535889567052697071163303906961990346696443412322669422394247994658319178372429008148965598978024149163378302186002070889877074425031614849597547348803787564840490596056243153664298329184085610195755669585080424208825452910193375955433268342126758693177564186376583945056312415551604815906210692714573407529900949806517308957517456810289297973748866632743471511521759674198284060892389730619835594125386405535807180514542861831780111674530334077603656160640918841921598193678865548479557396320708062432621422643089475161315601211694655121537093426836288941959841192446257548942594084381767212820316323351941146332923909112046347297752352620912612609615004723071875219131425934051297520782935697667784564213184146741669901662536128637577406679513383759697011207629361469912413914211130820216332347330824751586358937574629724906800206449327530347540137100465798556073502894842558877777218652468811354834020871157208208017075960961135646037760251082925639078481403925531262947002013524549413954955580599051075313312537222694547164640920223344964334621028971858613070952976067404366230751407927916803405274710449927710970089659316204745525766747434520204671818752023148409509426787574761181967982357730451886982004433837981914463665726658480821757897737968075692406476509203297886524864409087812833213893016456429128840476003836128992210570271017114485449695942479351780400740007724350242325358816229572331988556404687069409431728537767394410439682834004457335805201006193730186460586048762687943089937745540109167049725735353109791225127498512079786071131116653308263331817230782909070617874178420827816194450529637459992573048714832406731876533531418011127421329658809157399820151406671454527933691856659385268644781954806464780784590275156550515510377732310422093974405806231538585780693917997472327770071128369563806258680129620871999006949080681669375481233730880870954756588473509122339850709298151953913891080131512873830694105243773294598047037885616453508515444958661519444277607048660150397337905675084194382939190542989927681736744658398118220224967207111173462088338814313859206482296974020916339176891530533232167558599873803292931238761918735418512558731981957942759149532844192834026905241784778441129401344524946612151240419333331088928971611970664136845337873787980193275050340938216076595567132457116841370251443917467755325485185619121258859825625462954955644747207285013195901468957216134671493551949116812316740188680403367694916873715720858794980451041139024575590525326438661650021189857872878118065887210211838765630909578750050086635786669973524654479015204289407717711571281780846300058650288996354702446024455705536837304882696546958109464028513939511968228317939289925318058998092619116530823045358212175141570365586392894978993883438850336199268641642383216837217372433548361733154639675098183671371653786729944913178894763392400487867222487843412743538705492101537327781815964998712346496389511987348159640977439752544566621305745030124579795717455593206012228489578927664151787389323299682311420660084683863083935534586922743962469684959295699986926230700225625363529202304895084158765332151157906835033844000822536673651378581704263028219023963520103179481668230892202703126330638061114453550759513647329170387888438662372047941920697704239042566792620298157696501347307489696958915723580776743267182770446540156071925973375250545406994232868522254403284190716756355572971264332665835654153661299098964186168284856293935836373939978890553425073093312173526455241744731180828183835470487157567438992438494429079908782600707854775447440318638689909908102092367246509560175349502673067769874455749167156660816656105518216983900239004504356180471109969822988987467136548416611935156204496756302518766896615319115456121087833206468293682860111925507242548161584907675799739150843481917257417330570311421660493103648409896507765698412527766855601411909415842817473321980154750813387265567011104803812282174910236579140821672883330498207129860964322950279360287910376211848896232402326883531456910469229045769542884854756485687095824518615299347445006324751325775538056476010866759068986230486393616627633076485743737737453712680488188458612775328122512144642459742961092784328560026107712911618601000959656208246928950793134453409016168594257637746908836391096655233376484744508057267504327916316402893666633347580761533241745573541796140354668841637062389448333951840660189944320013139780402501374244400415576957190865990377429146774015599935289827130742656182898205046185034679640284270799207179812987299851091242396893505497393804903022537274624370022501399944518592706558892681225156958883473552592974679026380003534339290157520875620448154175229587220461255085123048830465201294942418429415008127373659194837443032772141627907066683985110977020819409022869669098341019128978700446044591394140138862263211583728735083438891833410092460983200271428529820374640127447372037101654680962922533882896971029471038507823336233005619462461248446127014182309060375853005584663129346154383117491977919454765501990140740451185024012599758740812479192040707143621033069214213689261649719409233957894428566751366978108375744984660240089263520318880350958855106117277739119765248507714096398770516095207904884962802829393486621182256216395346673306874586425309679193106829179287314481608062692666298012364672044309172269002555095126383004919641697701003087936274435258760016635769956794257731309435252502527477779258454262228639442571436237662548517748375561715077875994991418650573698747322855750957273952480251150828469980983940555181026759089562603737188676051626146900955252128525658326109513809716472467304974293201542469772714065780027710402569545659533215414295353042933036783167975701941917176744720493288233416163858193102654264971825627134067022695647233548675067958923120734348670671276181515378663178127954831376332011192664479273972428015510528629012594114587506717581600558560312339130341752781864197618465696528391720980132444002241688710045032205825182715531669514636450764664895076223988800198807858261776662377919744187525467182459315202990330826022549336475060716686802292971691292196136252732007955180119233258956940708086898646758326583308572995731082382810522568673113217389482877316241611273176336792410538869366429969118889142898323994800124885883410785325813903088434776470334043758766257432912076194495691881246626556156485775326368685560269508089471369260198305876099839908568058493855376409623538328409773360813262468285251416441728193875140780061115549403022460004130589245501538389432321276644982499760609879567894241205391393566359120837965184777448802150301426915266491269152626750746622600881974715402419613338910617631611364949636997870467142721764898310237545243353465591214299153731546519530817360230100922488223038281905467227828298390863395730623311300197877857231650878597347544681677316422706983050510534768272993829681277262519516808369882304324719176378170224880687742009994330050040040089691203918418553127308295751356243450497908087492959963535160085050881359616716034133683698146400638729110028015555507577894008707486116335227628760365155960781536850508369302159056229512097550501101236066958049195174768555718320515360011304310924502195055782641605117982329562649728560239018051602038167509581593720577712676912698875769155478273916492092713876276734723525971498059409042023114302543126215246716762131904688374748859769006898528937040602792838580982044798844904481417538683488279928167668711840406965464507788446099432539793459900413441208832309842202868915558641455469752274224389049694052680795850548062281941816472737390265236339492527019740315839407003038932520042717990918394821967480239944878659979266066419456472092394842885102022281814880077099299673072870396902732640041014334579708588189560906838748859037955449718681713257922163211398131599247163645203947283910457095056985851586192688831928278351355856850096649628185489136213437861271131470859481457511905715308321619624164664342414416575386387629104122444606838650811041271328033081866535778762090922882855963821952461558597664502919868322683149863680603181260032614421471647154283617751583806649617973184347450083184020143016705493327927901272607176378907312954146831788438526634639609171331815327113100284713880147248732846193913987656908605855047587646689818611663267871179407419472943985172842601405974901788189030298453784414096540579553689265488659707206881956049137101546585971963288007781361982140294131921217230906087085563824411426146410523247807185119434247392240215137993476661292467196907385643936111713376634743248062736940592205008248602660125092726667575373129240354067789606850817830321935264214718244133931039609646580613759449911529204410740122722794540058073285796248418108953329798479923
(Ethermine)
2,561.650606947862419897 Eth2,561.702569505617012219 Eth0.051962557754592322

Execution Trace

MiningFixRangeBoost.60806040( )
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.4;
// Uncomment if needed.
// import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "../multicall.sol";
/// @title Simple math library for Max and Min.
library Math {
    function max(int24 a, int24 b) internal pure returns (int24) {
        return a >= b ? a : b;
    }
    function min(int24 a, int24 b) internal pure returns (int24) {
        return a < b ? a : b;
    }
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}
/// @title Uniswap V3 Nonfungible Position Manager Interface
interface PositionManagerV3 {
    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;
    function ownerOf(uint256 tokenId) external view returns (address);
}
/// @title Uniswap V3 Liquidity Mining Main Contract
contract MiningFixRangeBoost is Ownable, Multicall, ReentrancyGuard, IERC721Receiver {
    using Math for int24;
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.UintSet;
    struct PoolInfo {
        address token0;
        address token1;
        uint24 fee;
    }
    /// @dev Contract of the uniV3 Nonfungible Position Manager.
    PositionManagerV3 uniV3NFTManager;
    PoolInfo public rewardPool;
    /// @dev The reward range of this mining contract.
    int24 rewardUpperTick;
    int24 rewardLowerTick;
    /// @dev Last block number that the accRewardRerShare is touched.
    uint256 lastTouchBlock;
    /// @dev The block number when NFT mining rewards starts/ends.
    uint256 startBlock;
    uint256 endBlock;
    struct RewardInfo {
        /// @dev Contract of the reward erc20 token.
        address rewardToken;
        /// @dev who provides reward
        address provider;
        /// @dev Accumulated Reward Tokens per share, times 1e128.
        uint256 accRewardPerShare;
        /// @dev Reward amount for each block.
        uint256 rewardPerBlock;
    }
    mapping(uint256 => RewardInfo) public rewardInfos;
    uint256 public rewardInfosLen;
    /// @dev Store the owner of the NFT token
    mapping(uint256 => address) public owners;
    /// @dev The inverse mapping of owners.
    mapping(address => EnumerableSet.UintSet) private tokenIds;
    /// @dev Record the status for a certain token for the last touched time.
    struct TokenStatus {
        uint256 vLiquidity;
        uint256 validVLiquidity;
        uint256 nIZI;
        uint256 lastTouchBlock;
        uint256[] lastTouchAccRewardPerShare;
    }
    mapping(uint256 => TokenStatus) public tokenStatus;
    function lastTouchAccRewardPerShare(uint256 tokenId)
        external
        view
        returns (uint256[] memory lta)
    {
        TokenStatus memory t = tokenStatus[tokenId];
        uint256 len = t.lastTouchAccRewardPerShare.length;
        lta = new uint256[](len);
        for (uint256 i = 0; i < len; i++) {
            lta[i] = t.lastTouchAccRewardPerShare[i];
        }
        return lta;
    }
    /// @dev token to lock, 0 for not boost
    IERC20 public iziToken;
    /// @dev current total nIZI.
    uint256 public totalNIZI;
    /// @dev Current total virtual liquidity.
    uint256 public totalVLiquidity;
    /// @dev 2 << 128
    uint256 internal constant Q128 = 0x100000000000000000000000000000000;
    // Events
    event Deposit(address indexed user, uint256 tokenId, uint256 nIZI);
    event Withdraw(address indexed user, uint256 tokenId);
    event CollectReward(address indexed user, uint256 tokenId, address token, uint256 amount);
    event ModifyEndBlock(uint256 endBlock);
    event ModifyRewardPerBlock(address indexed rewardToken, uint256 rewardPerBlock);
    event ModifyProvider(address indexed rewardToken, address provider);
    constructor(
        address _uniV3NFTManager,
        address token0,
        address token1,
        uint24 fee,
        RewardInfo[] memory _rewardInfos,
        address iziTokenAddr,
        int24 _rewardUpperTick,
        int24 _rewardLowerTick,
        uint256 _startBlock,
        uint256 _endBlock
    ) {
        uniV3NFTManager = PositionManagerV3(_uniV3NFTManager);
        require(_rewardLowerTick < _rewardUpperTick, "L<U");
        require(token0 < token1, "TOKEN0 < TOKEN1 NOT MATCH");
        rewardPool.token0 = token0;
        rewardPool.token1 = token1;
        rewardPool.fee = fee;
        rewardInfosLen = _rewardInfos.length;
        require(rewardInfosLen > 0, "NO REWARD");
        require(rewardInfosLen < 3, "AT MOST 2 REWARDS");
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            rewardInfos[i] = _rewardInfos[i];
            rewardInfos[i].accRewardPerShare = 0;
        }
        // iziTokenAddr == 0 means not boost
        iziToken = IERC20(iziTokenAddr);
        rewardUpperTick = _rewardUpperTick;
        rewardLowerTick = _rewardLowerTick;
        startBlock = _startBlock;
        endBlock = _endBlock;
        lastTouchBlock = startBlock;
        totalVLiquidity = 0;
        totalNIZI = 0;
    }
    /// @notice Used for ERC721 safeTransferFrom
    function onERC721Received(address, address, uint256, bytes memory) 
        public 
        virtual 
        override 
        returns (bytes4) 
    {
        return this.onERC721Received.selector;
    }
    /// @notice Get the overall info for the mining contract.
    function getMiningContractInfo()
        external
        view
        returns (
            address token0_,
            address token1_,
            uint24 fee_,
            RewardInfo[] memory rewardInfos_,
            address iziTokenAddr_,
            int24 rewardUpperTick_,
            int24 rewardLowerTick_,
            uint256 lastTouchBlock_,
            uint256 totalVLiquidity_,
            uint256 startBlock_,
            uint256 endBlock_
        )
    {
        rewardInfos_ = new RewardInfo[](rewardInfosLen);
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            rewardInfos_[i] = rewardInfos[i];
        }
        return (
            rewardPool.token0,
            rewardPool.token1,
            rewardPool.fee,
            rewardInfos_,
            address(iziToken),
            rewardUpperTick,
            rewardLowerTick,
            lastTouchBlock,
            totalVLiquidity,
            startBlock,
            endBlock
        );
    }
    /// @notice Compute the virtual liquidity from a position's parameters.
    /// @param tickLower The lower tick of a position.
    /// @param tickUpper The upper tick of a position.
    /// @param liquidity The liquidity of a a position.
    /// @dev vLiquidity = liquidity * validRange^2 / 1e6, where the validRange is the tick amount of the
    /// intersection between the position and the reward range.
    /// We divided it by 1e6 to keep vLiquidity smaller than Q128 in most cases. This is safe since liqudity is usually a large number.
    function _getVLiquidityForNFT(
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity
    ) internal view returns (uint256 vLiquidity) {
        // liquidity is roughly equals to sqrt(amountX*amountY)
        require(liquidity >= 1e6, "LIQUIDITY TOO SMALL");
        uint256 validRange = uint24(
            Math.max(
                Math.min(rewardUpperTick, tickUpper) - Math.max(rewardLowerTick, tickLower),
                0
            )
        );
        vLiquidity = (validRange * validRange * uint256(liquidity)) / 1e6;
        return vLiquidity;
    }
    /// @notice new a token status when touched.
    function _newTokenStatus(
        uint256 tokenId,
        uint256 vLiquidity,
        uint256 validVLiquidity,
        uint256 nIZI
    ) internal {
        TokenStatus storage t = tokenStatus[tokenId];
        t.vLiquidity = vLiquidity;
        t.validVLiquidity = validVLiquidity;
        t.nIZI = nIZI;
        t.lastTouchBlock = lastTouchBlock;
        t.lastTouchAccRewardPerShare = new uint256[](rewardInfosLen);
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            t.lastTouchAccRewardPerShare[i] = rewardInfos[i].accRewardPerShare;
        }
    }
    /// @notice update a token status when touched
    function _updateTokenStatus(
        uint256 tokenId,
        uint256 vLiquidity,
        uint256 validVLiquidity,
        uint256 nIZI
    ) internal {
        TokenStatus storage t = tokenStatus[tokenId];
        t.vLiquidity = vLiquidity;
        // when not boost, validVL == vL
        t.validVLiquidity = validVLiquidity;
        t.nIZI = nIZI;
        t.lastTouchBlock = lastTouchBlock;
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            t.lastTouchAccRewardPerShare[i] = rewardInfos[i].accRewardPerShare;
        }
    }
    /// @notice Update reward variables to be up-to-date.
    function _updateVLiquidity(uint256 vLiquidity, bool isAdd) internal {
        if (isAdd) {
            totalVLiquidity = totalVLiquidity + vLiquidity;
        } else {
            totalVLiquidity = totalVLiquidity - vLiquidity;
        }
        // Q128 is enough for 10^5 * 10^5 * 10^18 * 10^10
        require(totalVLiquidity <= Q128, "TOO MUCH LIQUIDITY STAKED");
    }
    function _updateNIZI(uint256 nIZI, bool isAdd) internal {
        if (isAdd) {
            totalNIZI = totalNIZI + nIZI;
        } else {
            totalNIZI = totalNIZI - nIZI;
        }
        // totalNIZI is always < Q96
    }
    /// @notice Update the global status.
    function _updateGlobalStatus() internal {
        if (block.number <= lastTouchBlock) {
            return;
        }
        if (lastTouchBlock >= endBlock) {
            return;
        }
        uint256 currBlockNumber = Math.min(block.number, endBlock);
        if (totalVLiquidity == 0) {
            lastTouchBlock = currBlockNumber;
            return;
        }
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            uint256 tokenReward = (currBlockNumber - lastTouchBlock) * rewardInfos[i].rewardPerBlock;
            rewardInfos[i].accRewardPerShare = rewardInfos[i].accRewardPerShare + ((tokenReward * Q128) / totalVLiquidity);
        }
        lastTouchBlock = currBlockNumber;
    }
    function _computeValidVLiquidity(uint256 vLiquidity, uint256 nIZI)
        internal
        view
        returns (uint256)
    {
        if (totalNIZI == 0) {
            return vLiquidity;
        }
        uint256 iziVLiquidity = (vLiquidity * 4 + (totalVLiquidity * nIZI * 6) / totalNIZI) / 10;
        return Math.min(iziVLiquidity, vLiquidity);
    }
    /// @notice Deposit a single position.
    /// @param tokenId The related position id.
    /// @param nIZI the amount of izi to lock
    function deposit(uint256 tokenId, uint256 nIZI)
        external
        returns (uint256 vLiquidity)
    {
        address owner = uniV3NFTManager.ownerOf(tokenId);
        require(owner == msg.sender, "NOT OWNER");
        (
            ,
            ,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            ,
            ,
            ,
        ) = uniV3NFTManager.positions(tokenId);
        // alternatively we can compute the pool address with tokens and fee and compare the address directly
        require(token0 == rewardPool.token0, "TOEKN0 NOT MATCH");
        require(token1 == rewardPool.token1, "TOKEN1 NOT MATCH");
        require(fee == rewardPool.fee, "FEE NOT MATCH");
        // require the NFT token has interaction with [rewardLowerTick, rewardUpperTick]
        vLiquidity = _getVLiquidityForNFT(tickLower, tickUpper, liquidity);
        require(vLiquidity > 0, "INVALID TOKEN");
        uniV3NFTManager.safeTransferFrom(msg.sender, address(this), tokenId);
        owners[tokenId] = msg.sender;
        bool res = tokenIds[msg.sender].add(tokenId);
        require(res);
        // the execution order for the next three lines is crutial
        _updateGlobalStatus();
        _updateVLiquidity(vLiquidity, true);
        if (address(iziToken) == address(0)) {
            // boost is not enabled
            nIZI = 0;
        }
        _updateNIZI(nIZI, true);
        uint256 validVLiquidity = _computeValidVLiquidity(vLiquidity, nIZI);
        require(nIZI < Q128 / 6, "NIZI O");
        _newTokenStatus(tokenId, vLiquidity, validVLiquidity, nIZI);
        if (nIZI > 0) {
            // lock izi in this contract
            iziToken.safeTransferFrom(msg.sender, address(this), nIZI);
        }
        emit Deposit(msg.sender, tokenId, nIZI);
        return vLiquidity;
    }
    /// @notice deposit iZi to an nft token
    /// @param tokenId nft already deposited
    /// @param deltaNIZI amount of izi to deposit
    function depositIZI(uint256 tokenId, uint256 deltaNIZI)
        external
        nonReentrant
    {
        require(owners[tokenId] == msg.sender, "NOT OWNER OR NOT EXIST");
        require(address(iziToken) != address(0), "NOT BOOST");
        require(deltaNIZI > 0, "DEPOSIT IZI MUST BE POSITIVE");
        _collectReward(tokenId);
        TokenStatus memory t = tokenStatus[tokenId];
        _updateNIZI(deltaNIZI, true);
        uint256 nIZI = t.nIZI + deltaNIZI;
        // update validVLiquidity
        uint256 validVLiquidity = _computeValidVLiquidity(t.vLiquidity, nIZI);
        _updateTokenStatus(tokenId, t.vLiquidity, validVLiquidity, nIZI);
        // transfer iZi from user
        iziToken.safeTransferFrom(msg.sender, address(this), deltaNIZI);
    }
    /// @notice withdraw a single position.
    /// @param tokenId The related position id.
    /// @param noReward true if donot collect reward
    function withdraw(uint256 tokenId, bool noReward) external nonReentrant {
        require(owners[tokenId] == msg.sender, "NOT OWNER OR NOT EXIST");
        if (noReward) {
            _updateGlobalStatus();
        } else {
            _collectReward(tokenId);
        }
        uint256 vLiquidity = tokenStatus[tokenId].vLiquidity;
        _updateVLiquidity(vLiquidity, false);
        uint256 nIZI = tokenStatus[tokenId].nIZI;
        if (nIZI > 0) {
            _updateNIZI(nIZI, false);
            // refund iZi to user
            iziToken.safeTransfer(msg.sender, nIZI);
        }
        uniV3NFTManager.safeTransferFrom(address(this), msg.sender, tokenId);
        owners[tokenId] = address(0);
        bool res = tokenIds[msg.sender].remove(tokenId);
        require(res);
        emit Withdraw(msg.sender, tokenId);
    }
    /// @notice Collect pending reward for a single position.
    /// @param tokenId The related position id.
    function _collectReward(uint256 tokenId) internal {
        TokenStatus memory t = tokenStatus[tokenId];
        _updateGlobalStatus();
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            uint256 _reward = (t.validVLiquidity * (rewardInfos[i].accRewardPerShare - t.lastTouchAccRewardPerShare[i])) / Q128;
            if (_reward > 0) {
                IERC20(rewardInfos[i].rewardToken).safeTransferFrom(
                    rewardInfos[i].provider,
                    msg.sender,
                    _reward
                );
            }
            emit CollectReward(
                msg.sender,
                tokenId,
                rewardInfos[i].rewardToken,
                _reward
            );
        }
        uint256 nIZI = t.nIZI;
        // update validVLiquidity
        uint256 validVLiquidity = _computeValidVLiquidity(t.vLiquidity, nIZI);
        _updateTokenStatus(tokenId, t.vLiquidity, validVLiquidity, nIZI);
    }
    /// @notice Collect pending reward for a single position.
    /// @param tokenId The related position id.
    function collectReward(uint256 tokenId) external nonReentrant {
        require(owners[tokenId] == msg.sender, "NOT OWNER OR NOT EXIST");
        _collectReward(tokenId);
    }
    /// @notice Collect all pending rewards.
    function collectRewards() external nonReentrant {
        EnumerableSet.UintSet storage ids = tokenIds[msg.sender];
        for (uint256 i = 0; i < ids.length(); i++) {
            require(owners[ids.at(i)] == msg.sender, "NOT OWNER");
            _collectReward(ids.at(i));
        }
    }
    /// @notice View function to get position ids staked here for an user.
    /// @param _user The related address.
    function getTokenIds(address _user)
        external
        view
        returns (uint256[] memory)
    {
        EnumerableSet.UintSet storage ids = tokenIds[_user];
        // push could not be used in memory array
        // we set the tokenIdList into a fixed-length array rather than dynamic
        uint256[] memory tokenIdList = new uint256[](ids.length());
        for (uint256 i = 0; i < ids.length(); i++) {
            tokenIdList[i] = ids.at(i);
        }
        return tokenIdList;
    }
    /// @notice Return reward multiplier over the given _from to _to block.
    /// @param _from The start block.
    /// @param _to The end block.
    function _getMultiplier(uint256 _from, uint256 _to)
        internal
        view
        returns (uint256)
    {
        if (_from > _to) {
            return 0;
        }
        if (_to <= endBlock) {
            return _to - _from;
        } else if (_from >= endBlock) {
            return 0;
        } else {
            return endBlock - _from;
        }
    }
    /// @notice View function to see pending Reward for a single position.
    /// @param tokenId The related position id.
    function pendingReward(uint256 tokenId)
        public
        view
        returns (uint256[] memory)
    {
        TokenStatus memory t = tokenStatus[tokenId];
        uint256[] memory _reward = new uint256[](rewardInfosLen);
        for (uint256 i = 0; i < rewardInfosLen; i++) {
            uint256 multiplier = _getMultiplier(lastTouchBlock, block.number);
            uint256 tokenReward = multiplier * rewardInfos[i].rewardPerBlock;
            uint256 rewardPerShare = rewardInfos[i].accRewardPerShare + (tokenReward * Q128) / totalVLiquidity;
            // l * (currentAcc - lastAcc)
            _reward[i] = (t.validVLiquidity * (rewardPerShare - t.lastTouchAccRewardPerShare[i])) / Q128;
        }
        return _reward;
    }
    /// @notice View function to see pending Rewards for an address.
    /// @param _user The related address.
    function pendingRewards(address _user)
        external
        view
        returns (uint256[] memory)
    {
        uint256[] memory _reward = new uint256[](rewardInfosLen);
        for (uint256 j = 0; j < rewardInfosLen; j++) {
            _reward[j] = 0;
        }
        for (uint256 i = 0; i < tokenIds[_user].length(); i++) {
            uint256[] memory r = pendingReward(tokenIds[_user].at(i));
            for (uint256 j = 0; j < rewardInfosLen; j++) {
                _reward[j] += r[j];
            }
        }
        return _reward;
    }
    // Control fuctions for the contract owner and operators.
    /// @notice If something goes wrong, we can send back user's nft and locked iZi
    /// @param tokenId The related position id.
    function emergenceWithdraw(uint256 tokenId) external onlyOwner {
        address owner = owners[tokenId];
        require(owner != address(0));
        uniV3NFTManager.safeTransferFrom(
            address(this),
            owners[tokenId],
            tokenId
        );
        uint256 nIZI = tokenStatus[tokenId].nIZI;
        if (nIZI > 0) {
            // we should ensure nft refund to user
            // omit the case when transfer() returns false unexpectedly
            iziToken.transfer(owner, nIZI);
        }
        // make sure user cannot withdraw/depositIZI or collect reward on this nft
        owners[tokenId] = address(0);
    }
    /// @notice Set new reward end block.
    /// @param _endBlock New end block.
    function modifyEndBlock(uint256 _endBlock) external onlyOwner {
        require(_endBlock > block.number, "OUT OF DATE");
        _updateGlobalStatus();
        // jump if origin endBlock < block.number
        lastTouchBlock = block.number;
        endBlock = _endBlock;
        emit ModifyEndBlock(endBlock);
    }
    /// @notice Set new reward per block.
    /// @param rewardIdx which rewardInfo to modify
    /// @param _rewardPerBlock new reward per block
    function modifyRewardPerBlock(uint256 rewardIdx, uint256 _rewardPerBlock)
        external
        onlyOwner
    {
        require(rewardIdx < rewardInfosLen, "OUT OF REWARD INFO RANGE");
        _updateGlobalStatus();
        rewardInfos[rewardIdx].rewardPerBlock = _rewardPerBlock;
        emit ModifyRewardPerBlock(
            rewardInfos[rewardIdx].rewardToken,
            _rewardPerBlock
        );
    }
    /// @notice Set new reward provider.
    /// @param rewardIdx which rewardInfo to modify
    /// @param provider New provider
    function modifyProvider(uint256 rewardIdx, address provider)
        external
        onlyOwner
    {
        require(rewardIdx < rewardInfosLen, "OUT OF REWARD INFO RANGE");
        rewardInfos[rewardIdx].provider = provider;
        emit ModifyProvider(rewardInfos[rewardIdx].rewardToken, provider);
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);
    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);
    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);
    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);
    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);
    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);
    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }
    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }
    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }
    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }
    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.
        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.
    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];
        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.
            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;
            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];
                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }
            // Delete the slot where the moved value was stored
            set._values.pop();
            // Delete the index for the deleted slot
            delete set._indexes[value];
            return true;
        } else {
            return false;
        }
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }
    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }
    // Bytes32Set
    struct Bytes32Set {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }
    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }
    // AddressSet
    struct AddressSet {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;
        assembly {
            result := store
        }
        return result;
    }
    // UintSet
    struct UintSet {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }
    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;
        assembly {
            result := store
        }
        return result;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }
    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }
    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }
    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }
    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.
    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status;
    constructor() {
        _status = _NOT_ENTERED;
    }
    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
        _;
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
//  SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall {
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);
            if (!success) {
                // Next 5 lines from https://ethereum.stackexchange.com/a/83577
                if (result.length < 68) revert();
                assembly {
                    result := add(result, 0x04)
                }
                revert(abi.decode(result, (string)));
            }
            results[i] = result;
        }
    }
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }
    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");
        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }
    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }
    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }
    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}