ETH Price: $2,497.74 (-5.17%)
Gas: 3.77 Gwei

Transaction Decoder

Block:
16241691 at Dec-22-2022 05:19:23 PM +UTC
Transaction Fee:
0.066752967132480945 ETH $166.73
Gas Used:
4,029,447 Gas / 16.566284935 Gwei

Emitted Events:

197 St1inch.OwnershipTransferred( previousOwner=0x00000000...000000000, newOwner=[Sender] 0x11799622f4d98a24514011e8527b969f7488ef47 )

Account State Difference:

  Address   Before After State Difference Code
0x11799622...f7488eF47
(1inch: Deployer 4)
0.857539003036684225 Eth
Nonce: 1047
0.79078603590420328 Eth
Nonce: 1048
0.066752967132480945
0x9A0C8Ff8...D717501D7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 13539219511371119646217722540801430036755742231202013362302682876014214329955268116302153686647422707129244675825177628519862048274713895723615379932000245618304213901612076683196759145300890271218204593236397634659491018497742752196485255122152337139951915776107431632814704643864675332426061982426287433864648854266250788132673210238447157265925350568120448356807359832686557705271221914281138793711500412832463081243553263717629907681749915501220284849500390753831730112390092537800785347312609919940416718154553252036466633042225940039891546290831576978308791739335529115355238280634215973478380313371251513368919704589807379698293209053723926953151034706809013387898542799475548722816985716643912787834870848703388410397437628505288893838872984672846206421381937167948442971815084052046757241843749184272965128816078873413545205089498042969834050222765685674955864085974183531507349013646773706350020547481211886015226937034567639941069368685929254650206482898410827465435068717024314175737050229002409788881949021881505540224736216653404136229066970997459999230395692012514739597179113547320878516720301327118762075977082724536262525402460834722500187633380288249152887209314866897383452606488539887836669695804107062875027869546355369848610671725962212178478419391772913594741248742311289920050117496403764947370002414167936507027519557713525726364816025999258895050863230839178348555252598895423800611177687626635920714461698331419881650639466055846831752860221818817879322952225297626234958963885989843244060941776800211577011790682326244144493920697960499575421480286622543296383653142468275693659382781010695147473827315794878365043131587728913437344848134829128473026868602142675399766282396628055026911914502199137045029133982888536865229000731514665575776272092755354960173109437183707550684796045311906312600419131941624157381662660505840423883052244003527212586680135393705027493385177704822867799990985296142658360417304901458047500292092869489333847609883232975927841565519032816388073720953870304701962352456388168477089315973978726507872885842280793865056972290274765911200458378894236014649087861746752690665726148470917869835499686805072597168897591212144754421246225829114691044160617133380536750415091877718165135854697637803486718511024680346458462980747811227545795382323193081572161637563381285625213218467921075526000571617024571301373409819927391561841025800559031383510956715468948696860085866896496939268285154985632736055601531969031537547890390573151599450538786594278844868877044782867846592735664981028461948668100947382265438218034808714990169400851331952837878953310523708292993746921125342640650598030160607679272552669800604172066253959650769173815963273865410571498585521434260344399340785062545462992826673501035394175646135074356232153494019645108721546368429115774208257343255425175256610517367830896062899598574776549206487881280263061256400469374596768270169543311638884209912768973341608685904866154135960088511802421364935574050211925580550803734928222736738208280478746984170023284679035703702274098918294189545767776753290337907331449608512795205473573143451872011208147932148378333131367036212267420223674926654455170171124604807847633981933321421193818336422363413949522290278359093482025825286273814663046788871174155861607259361548280239996156248691261799136086626228813977678647267365324164390494210550187035275519798091179805393118802469570265145771644788797832177730123629941006908464142871350527390125897309027162515336852050954270731053106898062679008139882129949351862245083065218413958334050802188762369870654152256891208856867920497550636325466786603703193469425133067628306538831563421540902308480760956203774837965467573729061109577982547508020649739353977608266751619986082630850496472955319152104327477542708798301598630405717522249501838961950698012677159046967287644861557270106382478961766097501245397649228931479988866791151826492172495302625201901968583726483347278247665595474834890213738919383337924384976868086675577117175702662651917810740954669084148544992955350968195063148486006020380487027190598460160382723950193450422024260728318209800775946137244500023427436892376451910750767745073649221670349613731157335267242232922293940566134363200728033177836148123477672115393100671967873179896618441458133694813399461755997490781399049772221713574513024930990740319063955293986677736539948742888146104282576718049918744461071625035415362870100189361637651156669509338468342757836510058597276882964103582277456805528070877305323785761863952994117545856012784145510220203756735195261690341663442843545752583847174043111202907513090838546455338542511777198108049375296798025187167017070149934400475992052491436934276413716610068460085268392830582292878101498213121181728760176308365052566245811708871846084204938557972303164710576894122383256560041594353032425117201983121674435001463037328425190389270086216014852340055659343528712923707417677557522838942983730582742929286458291841120706556056832789158063216293934792606680166383881297819485259085213434617217246879243346329856726467652631612670574551220060836844872924090584364982245578989487054359055994092538083984136970692963527519772577219581229947131034242627885523462901057243855187595349609276402618309570815273683156567609372494355588602798399354073378884696337758507723598101826954836987730930407431141805567302022913823336680099939192748278891788676372382437386861529330990621223176928104053187115496886533559027708243570941213272463785769782958743372942140355582203202324878968260184528928178238243099369239811993077497801193180565493356862558882100063030450950320498864025036761296216669544688060925134895838029987608817053125670772513212751252853917074534603621448282511346247193471873468786205728952664999776971219983528264870627100766548701575983793481674656630612672692285742071343617403382923779956644884797518730305798352077996229984224650147733284896270300721201242436684750227417905442270085380851917846330306903938197070538244764538791641124596518287653358574832733449615009111651567677360505226240232914543092763622778818017675690402129996319490584956106884203748521951864320009124178544923948442090891548443042882831459487805257931975135434128001091489521847713312572065937858546906782224074161430721713131964264070782827597019151646162847957429243600549940102435765720364718883640678982368955264514421025753369223875334107529945402689695883560066716130537598913615602417439840011374333499486954192953236580136403664074303504471904441241191599687620504416414417774554708780879320776947481721540513986758013633306805122928346131102193292281391149204791093649990973471405071817893200193583380880711415736416660718661249013772705942716761208008417049586399813449542588828819750433205454794744631480975361930680130924429893437445595930851143482103299912445510966963697149556819560441633651504703956215617340025788175406121182801123671200358052699244877243234439462438388746878503262687823917081750512534623854407119716561292575400276688192806710936563194023908367044900465859450624443433518538843337368692252753910742487748449300648321149792427730691466516183923228097973925371433162504500794479400625268616060094157572859504796848520683894473516630362243170093739409341355854401528858678291508913811318136334440357257933196052178970516869834333324465991264058444857485299024192364998007401404495230639860814496622783280447288798577558963267479113106191930545904784976406221856700948415793564455302547107900776386388421663614636232200096333601986180848983981653923766936917934502379263436370380941889959667168689919802307146977627116184441670917464316552077778654788522811791938748985949339474681102681323602211774833701300128827337046326905261800004443517989606302885679937501026099445394365054410783393514780849305225156768631670374423475579758904402606660142449378184107437661699771921384571074882735892287448085489818886902430428464508762543854335952538631490721875520075821180818461584284929930242156478298595524895483898218392578416850807141235145771586946048404776847846516769798142084685797380667154561830299110366259921953876376505623590172997320107300650190389025457516997753550093529161124038574880366384166540967883202090288774195851456575055633066459896414525543835239253109593605496767486106732951324318970640798434050520279883341496774217958961521673760559454252037894202303004815388680944049144883756260410898594843318003741079574872875678783829446242288879966572163151658241885216547012998397072934760604677038028444705161768851932049532682520341698357425819799692055347158954979559531499822519042691669766810029248071819354664859463206842025135313439662037996394673837377712578754744291041527041609745608466405499236625258607080110755067317169958710493459215628290464023000298721419235599050997493774818263207698156625694019717435008771290178337779802468999929168038059271982415215252095751280855623587767082447365408835433342051216947018524361424606063585997110655633544367629941647246195020321839794826434712618050116377955125172830679083301111918118106315456301725074611752665759272036379575739072601315445080964857661262938913870060466952794524165106565244742209730269155599964118950223985308051375770210516521771884064283615609403033328320251699303069648955041695181743207861580195772450828262323477778743874509882824574802948173993225911932564277576205670838712026925832717900645394918437794788668713433595665157064318064534912727771185894542374150519632064233698428697458589758632943310140054755365352238326786406410564793023103732354682085288503843111875337980415943356818409436622011433561655148720442907136057541446957416957329441830756799939914691733162436705011958532180426942125279714706455558618409998636672550629860790142174637717594914539710575770790436786681415993190923297863037599858225662062857425538191024723024954291919111562252964872463506778643140542813440256051774034193808011278694085137431655861479886365499645234646276042426329740947512014120162895580370543704149240883824343349344041916371302346690910368273044863985646595411833515421565876692328026393863341836870648601773973550135223497964051679077724997718182957669368065480035629011280684952450023746932194582791822838555928425921186287175651110182095688051275298993493335519274355543651332630355609590437162064697973670227450576374574106633132577917744505115621836178400826854188063449585157143505718487650378369231234111051129414254382131048639949814574342323655105171462693250779661926104740803294954211781004270889631028841285158137182801908541978203335179787140431222549852904025691270045387120112882381294775477125462161585883539635100425597166695278113428676680108873830164940916661035263017029969802324306855594661401993498514130336891112728160622741683274942106140744817898649570161092491859524614417149809429910101442504118374769125854466427711854226235141878253167619294723315230049313707892069652676645090911073713618116564281247000803578965892595715433276100309510606071104327398500880053411722563148334174858747513174621812246668455865420423641912888973598394440478103403270933805512839862666102069792484637648474161172363857873218090437797827735541140913917953229993625989441325072566935747169997387864667613507653045012947573491222725935424384477866851654449792755224293258540791507752961856938007731081318686767998501114116130399718639005151671991847254143515063381453583838122342101184852101860361002194754617848943169686138444404815218902838058736283365374918416904991223527881084835453164929538994575491252839140704230371710077967505338328953516395025829607649762432261305539631574802212890695986578520700083170557384443857307977050169388088269632106047874013946062699873634475758727358275619556628826766409307228329004482747548254330020825919431975900945651391158675342431756123622304759823559950329960072179769971416921434525153675739850073184172120315439834493725966775938549008751541069777389493705605277537239013520792183565793081126945941770302435711747883801683174983965118519112268296978979632968849011386621874175541804142223620559330582884730096561278417506283901326060841028375703508199179510059412056270152166588628839415808469942891792738766024264514062691563890195651063641336780481355772300217982235968387496748804759543772297245878102545111991010997080158416516643889450927229160858012574290900851440185825286233789681837603795297435145341414837075439469346757222212618600537304604537908052800434764468241520207863076947272942667694484121195191419410051966267489274277259316797876830980620551239607288108926661003024710708442304382942141596305435876755606659266377038771780271115783596076200392604279770495275552569270829621832675176230470859956696997422779528484651514087505831934804178135940726191757375966117007850162091700488669408104279182737941156960756596086806050084158701644561316600493517748175133499923719687319511330276820147300961535261875607904171865656891590132279859703201285270530542120238819732490346474709408316331269487787547654270796558781090772637189398256439554394732853720509192886136104219013277797431691070006695909027690019909850686665857505906766277517731576266099163820232388763975293476328803485573153093364445854794505542636803451508301467508359050571959995398588521090301754207474882864532227963367701096666739071795793205873518986926440668806984192133723572346478551044113642301650365839342179144660724517544434682824076923611994661024426226243564972518587266155207105278252966986410195405182050393949311786663528041562344037881699227321899360980368203684604380181606355227760467346147409273948600122540764316365974747068377350917359794834383181498397296371285955745319467089266005787800640609946410560990587463903365684086085086112670614885811702940807008768890259476773568410048522030035125887642009477918246431607494798620751799189034501059122012950885589076259110679171136294938128818477789640237268991436601830580417614209312558697651258701119050236589779246996196956968809688251170792377908119860865727398289035177510989657379256148645165561288974381420240838249722211456161876262341668686884307809326807289170745132269578412199593669070517037943967228405073471367348889008147402971887569747557341669604031160102242533826259961030459725752487062319403801149924996769468681270126190694274705907662273358766175601231433010547855387745548737146995279129667779580959195862923545113530513280433200648782953527782815762352371430061169866103152210585008765604892110138868465975989584045189888107336153422408164919675363934873823379753136232510959806335438870394915211257489715329991623150060778438293762475025882200191467449807725578417002077845920208863081987497984610574125758568664964298506125013530035130648132847875967162037640711304917033598617457455551207021892700441002327591243809228934419165596816714259868405466301803221713801425486679335959432910620091071825178090430824251002785356836082445475518984441036732097387204984612612852427554325310863494558848257112290148315387649894946904738054130132641753555733359058250677367274110707800099282284792176155442906639431393021002389621196599913614058543024708486476616453365850109643244040162766236641865635288877466037193105825872709710316469567018841534424509958933009933924377168381029890097314030347350346954568154780237183027199817790626429324742717345577826964323634006394338875822434552115869869538870531484134794385911916248952750046819994460863155813641487837983285003611097931156162879842043405544899020498409346790645259872680661007574455830386769087785567530241036609001081600458798013501151890691929531018004808603482795613016274205833798416136254614073283095018891080385193356148398365373903839807441580324635534068608007112854711630030872789280456660893357308510014906908252528622394511060086102151035635464854544698033111303905949312334690969163709700253962727307320495411108721815977680909523012903065340152300357531443670688894136763875778416384790208865375675117960320346957932931092887849850413274263002076330219766467626558262723608043626561402317514998951285301415632553640009186809155279704034715174952653917468080308984676218663791409637331071822213204582415911057027237601162182310373763596764847453745830125408195015999334673422477579859787873648075036447049385376595434326332969273894674384443954000652606126448079436293431483493261664783095498016334732846480632102200833102696049446122636948737811894803495445601147785208125157993551567586566752019358215035181473330526332554386165954536549032475627494839116936330487896576711582547907666019840428978634841582414221864818121337960685889489791919892588766451382660282500566480742053320577918910049374656512255956187220695774342719289452682363556292700034942581339110726219916678235156519299897510615164826515190215726428039509084714786002133527222834171577382523977896105450837669054604555261934341900517193260976856822998098744944769096173744242355899715243146903199951441598653830229337969696924218228441278904419698321539968506731825639560299037147182164125564560288829417331532629641265793449805436346864937289114971611034057498503109401543737127824921491181054232077215322494515330596506731314225345007742658831987823750813791898313378497562007323784303162005532239965820367889542355460858182371872648064787056949446525359402556548806558912052100872350923365569781537186193074360504158632608533550492542019475191524988594112023492797547631173799360070230014748087135598039827876684969741925070606488829729464288384758349697902644510384284392500896106376929261129802496609398756028098970610767277685923538272039050324170414764036543269415513978334572813167624950701934419405915611879464207893382239673678508337402634233017271051055978376750970990718234937688841641330070385258575248157059981849064296285358820327793563149622747838305120588247568790265760285776034835219985094847548343403336495010265732410288550218722638766265423449327893286740451675014625316860672243953136944408178442238737494692436660045871636526764464614550474951194639611743794543450205150949091366304611883556605282327253022600361554986183346609950577873847726670532557371237620774745782428042667443147848832866172492046845522230778254613100457477524147406531159357738112978518359295843753181125633225233351556061020116291347928883008886572880056021397704342620772051694509094910225660834963030450142020438461408033964109799043912735318476391586736929801161706970852476186870690602991487456129768799311164164279875091100845854099392207115314788380244853510722329816623350337549416951555218149100439147787196998195705798086663893839711645903777148466879472634732979796441760081030563940439297762650971318199010417948363590452655740056132070106801981227883218780461005161479188995655539881566459462371176204936740661811363876141068714598460508053432818323709010137541978949078125147364368786377708193582521022423972072872551768614108609124222565485480320663119794971213301043700277172695587078458391254227033755477211869122299230679879259440319666756268208825816872782338794466102050453180563282907782342532743591337623787337486605964089997810673397892537090827906433866499105423857615876868636233852214274083605616706355262828465384082795489135464971017698176850449452362321781403403739910974888448378139961129866655058357959106163154511978946396997074511073624278356991109500758584398804473301347952034441898184257697471935385244313905365426892033751638672955707081701264517609340562202125144133896166551969481531407249971314590897628687208804490209262603797472398520076352954477670898052351305005860191482850010929616221258819088867208279452148469932151794645327219697173484478276355023336691192307294110234619327432195627464536944343640129338514794131408277064360854390436336710649486514113088296521775768409291079338223298130262451875591148034697500348059794941298031786017017805820580218335185316430070372769218982765487913800055775536176192464154779020482957482357140426586005040993193568231743265124344153906107379888034340036001202865516480414866815138810897383150242113882126398087743440282078719927969600347961545713315100452587110295276803068291216879363714070519488756093411678578302202631875005657050612299883021463678269220747606229542546825861557630839620118248970084377415294568475134518311191136718518498963392400049425894005432697194282489289838852889123457073910184866521935334840070235172484636243659103441039502479694674128305386827714546072610138518736179698574407478594020412361454555485901308745142930201224075421518209549755428938706970614197279500703731238773804207562080161642960201855536266500502229413619990189764897120711452001442946682441064795930778954841069113055871316652994105829377538164418045395283171203347070370267417357194162198063383064200969692147842148012149320654932900951017883980420980828650066519557083568605385447010956957488637372861510804478192483704926552474137070161204735191652222797086350790966176284855390945213952887502447626590560722791050172339210338420325115349115620368641401435558172439738442066602447100895108229861721462327886027153888016407819247622301994155219314389215541840278838660054992282573595810203994679381845775194665045153879799610353764106177614455384813034394259028803135534965281900076040026208038941279069942336498568547005690955009716404060137886586603321425930328641711524768688600986934988655146627297092311822565478073903859342379240403409828418449227839775490478268213498338163986847680759065053560432916298146047416559150313661655377642342227011133276372587061444873168262752386613313313258680853552089098964266294218698824337335488825515047137229739886106037355245780361131162198307743402234962539093477838980829563475632742753689932348181568243892195964262693333814510874321374290148145130484530273359217915183653303614858288012159380555321035815143450795560424265055717894156109660968923772724438369628740805744207337058839304770635199932521752111350951427518330847219134542017393724538312793365564299109930621899049702673085877448071652870125964553615228412531935862435020934728076119244092765178319425669088857421132323609054245268685566274381629805857843445383694006264322175806667052707648540468412955728876908448324528634239637266092483101656376028946240514772644533313284716055437406137958200348747551700335765489623324999893075663390170218587870771031481778690405547347326665011476522719168583340356808146369991982780154556878448146179218953996569586470869796057085779122502472259939576396390122738774630738185382984591110702395495101273946912503194000602616662354461974691052350319591708270017785969835509025632560295653527315006995489577966624952885785003536861170855733725811933448934346359880480664610649558446680336968032025653511838390574593390781704619369254288460298390299356887382720510710437054348968226751715567609303911591015357306132874192528738966737622754525623979400028896500875670360643785535090054158985847707622782005385836822581482370864071723664639763526049266271464898531291071321554553146659699398470021899291282962215059871976211466045587656563377182349453664447232308417039734231527154447178390876085734390006288428140292628665529331577867008679245385264031983729906769282064918939566910680692492155055902711305685543805443248993077112834434082698253767680442314521314327394918593006870850019448908015537703592410316689195806446479972017486410675031155485278789328276139226166964821641882291585775605019732546483377276544797771066440044225654286170789316416411452968735591407479520677891451588842821162803351759832541963457804972732634825604071809632873118722799069533008612245807644285966643138866045776822868236296008732506878864340523800225721684036432294336742034645636282534648104554355805660875482658392288401150603372477771661111800171682666593488511774086031406813348080328120642747427302160943896733560068359979985237421650837166044762101294136374121025851264475641761591033634800077955695796789025745421702844744033405610116818187999647698081730901364228887678713503891720333308232741312168272764393929532205415060626514610044589139335135782480965584935642490822725247265494748736521582364476942887960908197507139871064405652967393759632208117123873346116392559250329036597977281451786095153984607817917464036970844001547904427892765960945253026029292685118632601206441335682390069779844379631169429360462002329311318559840718986770794833188840918408639613214341167256817645032630350906450943479475463046721265577624276179544085416528631092897906527575406095915869480456401332079714310743975087604912302270040234118387460246465036731937625193405657430111202601743045240689143235394237173785337520388351562707711583337744072108999462365792978085373715695944517561514337499048906148310239494352489216076708452340585187980827172024717991859035458829171243533361736042070645174652118503055267155493322700879142890862289073109795634316913250265076471789890954960284556910123211728611172565696471925392038036874171916685055450854709958822768823905550413409135444364415859560383175358307206397629273976456185190616699168775707960711307849725651658225513552731542671256138337593467269357561425414190972624774689452148868378713431409961403924562040969276424979917181911852263210199752509388128018847852350033336608529088375460523674764269302916245697181518156962950050486669615557538608027549696040363894829398717752161815345101557079948561060635363842239934118348634646039122116189690362335511484193862109802732732886731069281253429006685657576830197641796589389838105711801528112979020038839766296733312314513181502372586123636991369263099042179486155853110814499999247077069089455893506847843346402584168260869651015910594020794356974826196740468686703641384109256840617827216193851949405854182848071095895283500345037699473470721307733741149074712776470043602527923659001298764773581379761993055877972198752781098693138993025606673872594416783920412570639019383921640718423983531936165198760455276078818629405213196477832269698278705652073430103160071799472143056040257316788894184869152535953928551787064744321751552627389205010481031244014667434178812419500559402599958520698422279018227773234613944910456859111136776256950804949330157137361635972988685634681506355185393519134146995801033175895787599115624899288853900088375950161149879924903431967148495582608434484525432920557855956535147688939587744779937601387162028854958337270337714728801154858572337126695160488097074315320898237684915908998225544949720639591917638200053576110703102497146820524243596057582743816208884395027897794669034528624867019365071085062645927234344017349089589630723968144916440111409074217720145539123222178320547876604659504130251641346575035172415505934458380905445420029982867799857005099344155566316976671911443170988100324882295729730671924814611322301280420492170700583061719690563833520931912380387436947861333145466401332301757270553504673901519868175251754132071528082371093707577408748644619884241591029972337796420330588284607371234979080565989520610470297746918710076969167116179064706833217858541245020898336930781777000881750454637163362878714292661364477575211589074467716455690103724722828259575587394778995452272235133415005052577122002565744773870258254071256001438477485697671863361560930319062252347755080072737119898669743237242504929288973430327479060780345254243711615461136142748435511809491599878076303440751591860441622665588702856953615389395107959996315244312221167294408430397566600525799489785166319017814612695675164710969448246830983996202643801575256801558572294034898325029606351530112772007816316670616285961165884822982223825537175736719164168766169576117829649529987435380340943827813553146348405567300438185612664080555719525601062019593682365369587181016475807077298398814986853292194251304467992466103536182069928182015687517381984996414111723131640688902140850750052036828424561242046042062632699874621283677120827886408574819608575282616103385474631636834122308800259086231737761801310296755159003547533591130617441475089024453925986423031746293664375646997796568483935411244741463730357468967196158229546081952925106780965494267462776852917621654625635410211889852761562538606472133981004644768342830762970401942750470766687126065791391833229412213667403585897152106091651224725766032990757212660258982889016292143181145046027070225813049103628386061500614955912563105414817190656412448558076379507846302739134190886490520734509103677988439508453407866636677129166325122282308877390424139154431239546254766232951849277151952836426566821786544755473304170153441657833980987029839297845473182341201779413668496359621826295192446773247405439921285347964293011498600811998287682110166598265495686575234876150933541850776280213758545131353924544499777777340115326536082889723590782932894653251954063560193897516000534850897371186575620170976578923018012975819486666522987886629385793067366170214325572711287642549204435724656644037643130147597286882654497229742282533902660996286256166154236333681518360231750346289689121341881517557307564341791512410664199427939600153278432064561502316267975270719618951396272496547442354722822682533974667106275805752256604557802061257749858478750362272368444693393303877741384143224269780064007955398493532440781229196665702211072833919884881004768826431069474496056327488985875946410183825397953652724183318864411159109089033907199799207145180779427958230773027404462595320312389021487178931393480599358768625903507047369813900088416058458499463053296997868598536813589327948646786033227006667562300904007641051062365177067976885117436154598733258395710713511211164207932809673573948617889176882941679667580053246954717281244868335748736467247962446557679451020962298604750337441358525746802128171975275386860431751524801158754660773589180813443855708384159021076901071297517136380284996994393620977169012001310360055285424383226723976850939519075381906068607148999455238029130633825693688949728379329468488560983676768412793325517624829871026057040871840220158173185396047066220051405010969047358719882677001965877497872783318939392390284612936492050486054469060039992581731542152025487832851787118036534689282865707144179046710522710792173995501159813801306417158549165583337016755974416455544974000516433011141071691714235244005100386671696254799668961124175223028590667280360885257239891325699541977049772033821960925821131541405052730103579383122701163181389908169834945956223015392988440771246747096577650816075944987313039413639300477725116337539266902875383794334118902812304168070313561963920567166099607593065453157556651929168175171711078855592396221493568042590719271297025899878991365546221352180432490944125485584224827644179408820669072869886335604595731928580833683178475770145635616424404794551890185073355394759915680180318775210273916161658599119454330203417338162519293204998648068046218789302143651185566178025634808327679402217613419636272440114883151256996268383708360503613869239937235950461608409526551815111437434283597931880040207868065464734434347616431122695233753466167533743217064832228982121686916708471672219533604827124048142392655803000624679057771986003618270202969207981036591689390854376945321814508801220043033461182403388452151974143512440455556527769508801975006284072021657601912796414506839559161263250660714529270190753491583659459377097426987110562475092075679926086568291321873550785524849560074763275604379958126550361503503237127600626656609532370433504242142413443596365391346606318269630061782851793625765100434588298842592740283146217955776861673083520657710054158566518016301284136766526870416706980449780983506063797546068336274950240357644870602982153105228874972596881586934111349179216289311581923445331237266102640989560082237948811985143404492134177250967484837562573285812623268120042353201469626494192761972663472156910247933317814925207890570427929287815744310179194023806075606330099499279134952146041029645798343661068995735725297500281721367774514811894961486140340018357357943031016143449305573434967251131590780050195445924744298173394888225487122142779168765617283125639629893836421319871412036811656595252317836378729179951120394179482242506593144962587436265582228599666308636844521025147540927975044334842799848827150756520601632647524366705165384943254513619573196155353507965991502520774059912117251231924805798158006214108300849141025775481772711296531545812096137472027027959597992691372578933630397931380892591580238874237043977084212232467921542761763982180429965114162482171921356173660348985571454316188561285615307681943462817473341532845535051930528023826908187103814054368577053284497326627358077834341734387585409324639400476888126918081404201374682731099657294382922665138159429698899103650397069787841389111981206077269047385674290692637498675017442886660110976826864970372353492383062451157452403699304297188142317634757910592337254495168295995202787827541305168180136536401981701827568955208370807433065113868564770008658612150410088351956788937255180252609111819962621106769977376828201035884816181124147947428162598282748816816823110149999337869707889535869846236889662995410183639099381723799739308040096006359601136134598261872945412240848338449233833284362175966077098629404598625873705124970895206513676064486941026714899640354284439281917224389501595775398485578026669779268378748556543489913534013453291237247554227216058331897958635872992430819126244684898068277683492487228905193010969295863193114917261895810117168131952079494668172815361318120139699151771622617417005452975395623738295360550000757573266866389569948665212463884517973500958081492834592608795976623088232367304101302657675717687717676726391360571571948067834686783780820450030535874029802682796228068916217238616781853327623668125379475153536038083307510517780676355582723334572767442218782671233244195675381462467186612634069207725647260767496092958336008897517597822806113981679826301402476884055550735159461014365870420848352690389621762136705892588516880535111299159190593063976211212572123465152017853033830797253499739096744854778129321521965841226242737922586830289787055350293711520499259095375388446351413740288844553625924786630004524425794090492047275440537184543767163908215759580806334180621697273304141318803225633098090079251804194399754454498811173357352220739320886997389220858206980110917183829331533019983976268183086761036132282315152787358205644204622955278169127608752988662804821812786752065719124347328041586005498141867127149222835667269077085206135705342048151432368275818556645354232777037806709904506468495960657831038979631292492406393653533081741270573479893660958878089414289878156514760401237792939834431231453883621432142449793158254697730387102997700246646575363758323225872533142436180411208634379102612447293069144405479998260808770425904178530232506470171753847119939661008094863378323824598707537673754233399788710972695656693797683604844882031286184382969490508436270566390637503736965541511792744366292150334246372209380256499898318356400852699900330815919116788367720806159836337824153795715066826532769176464399935896201054979035889604033180843016158789129664549753383399099377102713900297631781340191264296916380329802186335833179888052996210022311069716663479039017163190121471383453481925574513704105941740173863980853206193120795268335765282187902031846636829593362149551711525972517290221432245648995551064088885077127801358933073143055918319422878663856626085067823710576457116081392601103369674770559223468034015060204205733609011900947961323146262609078870719811607188795724535424888085636728409208143770484091382394402460495655811157225720571997517404960157209901828124418800245782851379332334067620865326285669607044696129410916022700932449175044862213379488020685282814565667715639917969896318973722235324551519202107363844287100117545708318331342199887733370410476312723572648114691474951145404987405072488350224086278153607045291287008997615659692176880776794099832177634648835351705490442238397210519440261635803350712289328452104153713878958968119481127848506538631372114570113461574366152817527409426973742739020860769328938297170639837530927468745599362865935274399754535794087069330664590907838559295157661663082030169838379352501005027584772595297912333374435647434928823462771990559228993300006846747720820850945360097183751044413306526498447082907636605428860350293682284764547410337740507176040119218912403307145601345392320895898049510999568060434569930885312210982387344901793864751644377495433894634079217954279132348201462394942535892340908014377890544322052827495779544790539130729123456133199589161650151151215184936853951885292914213449393284097767444930154013566180489944157174490861681906748058157610206483099711753957126937853835677731439017175903993429403092531865158123202414054002469280783094221701293755307789644783786941753426375106088125226240417264750565933389335774730735959916974189760224171416043026865569160168146763169985302642148372380751033855906801837691676941189861513621538695268140038400024155334686098813313302905664856496890778011007373639824955907373955531513676299414081760432956608543020869946853211153892094901124223239773350969153484702773356752295863668954542082291726092063063383348450169309985078430533174314726220970649334883065227542234435392071032558598734344199055616240548450251955634050480223211664809402398354904236531098597643222164994986448655162698379084074764388538219213445786768070569197607688441560046640267989706816267917176130470527904495329849399101072021992723444709969396823499050356822562783584444510183631793074821469515783894232286306100617095569336907812119602037203127963405437360732513231406459294132002971777257488937465390745411176122351312157987712938569594572306728539737168959792830008907083441495097851583298331308522818309904928527637199456365328985408505194747650064773167087445459580571306532430468559469551677390154091705967034810381577509252986017970250272761370252195807235192128826227851878635274466426656737671043260704505939842919598046521493560168541526975495845210575836107763789767166671695068556889357021959353609956159060959702379863554512781222914895293938589431823621874602489752613262518345779446761269744315331086396929395925331382934834249115277759944072159939900866672971334932890458449972084557168030556729713422949107457566115368169743220467294343205735192597139700228969642513401893297011966381504741392109488826441720749822573477587060318930956244662257525607383509779949724872049299761913841010286377096224073891807245499769086872043176802068098824369689594206747659809412360179188782707592796757487726670462329072879084425085420382380653383523302368965693063263448969601142984895399330917858798122544470613238734749813837219818147257681310970659432355592862606395697399362269939049499593547483513413165189213317638480774356738122391101591248226085856741208040143161550700883471017366177633998613720743794088103996766925955453225884579268914222229041906290971620854406795188453477741034917878275293040932480593827119367936892192720313031932640313742328594152946043791031988554111412297525152479730431957119385688141089196154853673099710010320542641618402129120542606551031569078932346989066938630898916679229645291889074791929866615754267358863037786461886390397387448803019812223867054529101733946999274887109758806082863488118572750232675134540355043013696408312913849348551834111766788781507924810256853560337649778887992399159609581897100410443514846760603943413249312442040955528484086502184123948648882481563207795720970553590319220404007969677578024500738782418722630571539545404982941535478823377245077207090022252141778971091055389426428450356383812201488424357488278759900676414456190070482031627905790513903127976189391790978002360720588235275902347652131599145749664147967031126370970247323080687790815546992680867222270537051012695160759699648833875276564157082257913603946025180854810287195492552300033517183035977353532113548853297825466977835975301109133299302101991666261758237344238708648175159039986279134339259712625595714134160137036250685375571364877345691033041103528619235639526477643286272655479805943828783503672175949869967311557513014299231663071053497905765046186976072737752913848817135956433938605200061654774200700927735956926011273879689628325059702214025715510639058283991874914384850201590314840803112998895776496605860561469364353534266965171963700589197490905703693290636292310648459218093593508573035889132633168002289487015826010112908600170126210254579195465946397773292847224855107598155431275023988304920481485926832135506976437420842377308458165155076986747884382498327784405606622251080760859968730362141636086473451033787410287711530499804595265298436357214366495623772844299232036534199093277247292190386305759488954429516587066747840654070159929283856706265896452562951724113721414894534163638107429426352658619245038918366729557810531332141591689299444859424282740992709487017407611118118707647990869003739640218612985988957176524933240897562006584500655520137248901450762361563902596768097668186826719896383604101980880066530149647738127312322213720864458724566384948529674266441252909548463244513591150303183260321942361360734134111131892420682191649279092730548004682054983228723543879561904284746733189622259439159594328114011398499045047248839835561765600880836841408520841627163913001795004496117230761146331149862315680005702740786085253164916248816422327776309552799460846356521820987644390206821238665649397677662105995749720205047072208959274723294542148779806103696833970271980846322825626549030430352621447887353334274624031054020386462530958373265755816239284644300889037069738774392054743730113323981641212617604336002447206547981247127018081556142661479224669286002847746313913814934529278915722416762653611696242126597098583810093323926570827246709204758066761198985757074045349004304549818889776051381770716930071589147980572689799196399649765675227259313189747735725273350051000353424629957655742000508581229122082812333969689446685542644675811097634138711094853057401109333964300675884681892833248469848365936644994870738760874424475982383051767144555219304437470492833798248664670955008769536399122280282012352791517606360013659104882169009986934195129870872750471664719955130631996982819297345424373049104830890391097815508275754690373162803652922504428312011965295719377422381682152516731169035860020330340131749080753871439858090693835810662904091763846963133452395173506502677981912962703938258478004051161781007816365999717917409239198067416457970744693100703220773656257856505332865749693029317877721170935947540837789474228283195866991052554263811584843926600198422617194089533778942678900892836365139531362817632148901777256875659722448226110349307683069919669870511521948582721465820246424820868831706099193182125815320425395402443012106979692517591294034475926371049050848149200685179628969555473875698091115850298983908439251583220772561009205098947142399534562049894432456501312867108584981791807813994339261226325376566213408399888496741256965652092986776624024422209773818547480610783209197382030994765565411470928721073536038744508920944248338174188252948061525130298740622434026875980671244840142115423969435837768719664188596143110309379196099957258060382734943054251129937876393173067505437247429733204369688234866969066110029662697561020128084897103923
(Flashbots: Builder)
1.18096600406419218 Eth1.18298072756419218 Eth0.0020147235

Execution Trace

St1inch.6104e060( )
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@1inch/solidity-utils/contracts/libraries/AddressSet.sol";
import "./interfaces/IERC20Pods.sol";
import "./interfaces/IPod.sol";
import "./libs/ReentrancyGuard.sol";
abstract contract ERC20Pods is ERC20, IERC20Pods, ReentrancyGuardExt {
    using AddressSet for AddressSet.Data;
    using AddressArray for AddressArray.Data;
    using ReentrancyGuardLib for ReentrancyGuardLib.Data;
    error PodAlreadyAdded();
    error PodNotFound();
    error InvalidPodAddress();
    error PodsLimitReachedForAccount();
    error InsufficientGas();
    error ZeroPodsLimit();
    uint256 public immutable podsLimit;
    uint256 public immutable podCallGasLimit;
    ReentrancyGuardLib.Data private _guard;
    mapping(address => AddressSet.Data) private _pods;
    constructor(uint256 podsLimit_, uint256 podCallGasLimit_) {
        if (podsLimit_ == 0) revert ZeroPodsLimit();
        podsLimit = podsLimit_;
        podCallGasLimit = podCallGasLimit_;
        _guard.init();
    }
    function hasPod(address account, address pod) public view virtual returns(bool) {
        return _pods[account].contains(pod);
    }
    function podsCount(address account) public view virtual returns(uint256) {
        return _pods[account].length();
    }
    function podAt(address account, uint256 index) public view virtual returns(address) {
        return _pods[account].at(index);
    }
    function pods(address account) public view virtual returns(address[] memory) {
        return _pods[account].items.get();
    }
    function balanceOf(address account) public nonReentrantView(_guard) view override(IERC20, ERC20) virtual returns(uint256) {
        return super.balanceOf(account);
    }
    function podBalanceOf(address pod, address account) public nonReentrantView(_guard) view virtual returns(uint256) {
        if (hasPod(account, pod)) {
            return super.balanceOf(account);
        }
        return 0;
    }
    function addPod(address pod) public virtual {
        _addPod(msg.sender, pod);
    }
    function removePod(address pod) public virtual {
        _removePod(msg.sender, pod);
    }
    function removeAllPods() public virtual {
        _removeAllPods(msg.sender);
    }
    function _addPod(address account, address pod) internal virtual {
        if (pod == address(0)) revert InvalidPodAddress();
        if (!_pods[account].add(pod)) revert PodAlreadyAdded();
        if (_pods[account].length() > podsLimit) revert PodsLimitReachedForAccount();
        emit PodAdded(account, pod);
        uint256 balance = balanceOf(account);
        if (balance > 0) {
            _updateBalances(pod, address(0), account, balance);
        }
    }
    function _removePod(address account, address pod) internal virtual {
        if (!_pods[account].remove(pod)) revert PodNotFound();
        emit PodRemoved(account, pod);
        uint256 balance = balanceOf(account);
        if (balance > 0) {
            _updateBalances(pod, account, address(0), balance);
        }
    }
    function _removeAllPods(address account) internal virtual {
        address[] memory items = _pods[account].items.get();
        uint256 balance = balanceOf(account);
        unchecked {
            for (uint256 i = items.length; i > 0; i--) {
                _pods[account].remove(items[i - 1]);
                emit PodRemoved(account, items[i - 1]);
                if (balance > 0) {
                    _updateBalances(items[i - 1], account, address(0), balance);
                }
            }
        }
    }
    /// @notice Assembly implementation of the gas limited call to avoid return gas bomb,
    // moreover call to a destructed pod would also revert even inside try-catch block in Solidity 0.8.17
    /// @dev try IPod(pod).updateBalances{gas: _POD_CALL_GAS_LIMIT}(from, to, amount) {} catch {}
    function _updateBalances(address pod, address from, address to, uint256 amount) private {
        bytes4 selector = IPod.updateBalances.selector;
        bytes4 exception = InsufficientGas.selector;
        uint256 gasLimit = podCallGasLimit;
        assembly {  // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)
            mstore(ptr, selector)
            mstore(add(ptr, 0x04), from)
            mstore(add(ptr, 0x24), to)
            mstore(add(ptr, 0x44), amount)
            if lt(div(mul(gas(), 63), 64), gasLimit) {
                mstore(0, exception)
                revert(0, 4)
            }
            pop(call(gasLimit, pod, 0, ptr, 0x64, 0, 0))
        }
    }
    // ERC20 Overrides
    function _afterTokenTransfer(address from, address to, uint256 amount) internal nonReentrant(_guard) override virtual {
        super._afterTokenTransfer(from, to, amount);
        unchecked {
            if (amount > 0 && from != to) {
                address[] memory a = _pods[from].items.get();
                address[] memory b = _pods[to].items.get();
                uint256 aLength = a.length;
                uint256 bLength = b.length;
                for (uint256 i = 0; i < aLength; i++) {
                    address pod = a[i];
                    uint256 j;
                    for (j = 0; j < bLength; j++) {
                        if (pod == b[j]) {
                            // Both parties are participating of the same Pod
                            _updateBalances(pod, from, to, amount);
                            b[j] = address(0);
                            break;
                        }
                    }
                    if (j == bLength) {
                        // Sender is participating in a Pod, but receiver is not
                        _updateBalances(pod, from, address(0), amount);
                    }
                }
                for (uint256 j = 0; j < bLength; j++) {
                    address pod = b[j];
                    if (pod != address(0)) {
                        // Receiver is participating in a Pod, but sender is not
                        _updateBalances(pod, address(0), to, amount);
                    }
                }
            }
        }
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20Pods is IERC20 {
    event PodAdded(address account, address pod);
    event PodRemoved(address account, address pod);
    function hasPod(address account, address pod) external view returns(bool);
    function podsCount(address account) external view returns(uint256);
    function podAt(address account, uint256 index) external view returns(address);
    function pods(address account) external view returns(address[] memory);
    function podBalanceOf(address pod, address account) external view returns(uint256);
    function addPod(address pod) external;
    function removePod(address pod) external;
    function removeAllPods() external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPod {
    function updateBalances(address from, address to, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library ReentrancyGuardLib {
    error ReentrantCall();
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    struct Data {
        uint256 _status;
    }
    function init(Data storage self) internal {
        self._status = _NOT_ENTERED;
    }
    function enter(Data storage self) internal {
        if (self._status == _ENTERED) revert ReentrantCall();
        self._status = _ENTERED;
    }
    function exit(Data storage self) internal {
        self._status = _NOT_ENTERED;
    }
    function check(Data storage self) internal view returns (bool) {
        return self._status == _ENTERED;
    }
}
contract ReentrancyGuardExt {
    using ReentrancyGuardLib for ReentrancyGuardLib.Data;
    modifier nonReentrant(ReentrancyGuardLib.Data storage self) {
        self.enter();
        _;
        self.exit();
    }
    modifier nonReentrantView(ReentrancyGuardLib.Data storage self) {
        if (self.check()) revert ReentrancyGuardLib.ReentrantCall();
        _;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IPod.sol";
import "./interfaces/IERC20Pods.sol";
abstract contract Pod is IPod {
    error AccessDenied();
    IERC20Pods public immutable token;
    modifier onlyToken {
        if (msg.sender != address(token)) revert AccessDenied();
        _;
    }
    constructor(IERC20Pods token_) {
        token = token_;
    }
    function updateBalances(address from, address to, uint256 amount) external onlyToken {
        _updateBalances(from, to, amount);
    }
    function _updateBalances(address from, address to, uint256 amount) internal virtual;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
interface IDaiLikePermit {
    function permit(
        address holder,
        address spender,
        uint256 nonce,
        uint256 expiry,
        bool allowed,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
/// @title Library that implements address array on mapping, stores array length at 0 index.
library AddressArray {
    error IndexOutOfBounds();
    error PopFromEmptyArray();
    error OutputArrayTooSmall();
    /// @dev Data struct containing raw mapping.
    struct Data {
        mapping(uint256 => uint256) _raw;
    }
    /// @dev Length of array.
    function length(Data storage self) internal view returns (uint256) {
        return self._raw[0] >> 160;
    }
    /// @dev Returns data item from `self` storage at `i`.
    function at(Data storage self, uint256 i) internal view returns (address) {
        return address(uint160(self._raw[i]));
    }
    /// @dev Returns list of addresses from storage `self`.
    function get(Data storage self) internal view returns (address[] memory arr) {
        uint256 lengthAndFirst = self._raw[0];
        arr = new address[](lengthAndFirst >> 160);
        _get(self, arr, lengthAndFirst);
    }
    /// @dev Puts list of addresses from `self` storage into `output` array.
    function get(Data storage self, address[] memory output) internal view returns (address[] memory) {
        return _get(self, output, self._raw[0]);
    }
    function _get(
        Data storage self,
        address[] memory output,
        uint256 lengthAndFirst
    ) private view returns (address[] memory) {
        uint256 len = lengthAndFirst >> 160;
        if (len > output.length) revert OutputArrayTooSmall();
        if (len > 0) {
            output[0] = address(uint160(lengthAndFirst));
            unchecked {
                for (uint256 i = 1; i < len; i++) {
                    output[i] = address(uint160(self._raw[i]));
                }
            }
        }
        return output;
    }
    /// @dev Array push back `account` operation on storage `self`.
    function push(Data storage self, address account) internal returns (uint256) {
        unchecked {
            uint256 lengthAndFirst = self._raw[0];
            uint256 len = lengthAndFirst >> 160;
            if (len == 0) {
                self._raw[0] = (1 << 160) + uint160(account);
            } else {
                self._raw[0] = lengthAndFirst + (1 << 160);
                self._raw[len] = uint160(account);
            }
            return len + 1;
        }
    }
    /// @dev Array pop back operation for storage `self`.
    function pop(Data storage self) internal {
        unchecked {
            uint256 lengthAndFirst = self._raw[0];
            uint256 len = lengthAndFirst >> 160;
            if (len == 0) revert PopFromEmptyArray();
            self._raw[len - 1] = 0;
            if (len > 1) {
                self._raw[0] = lengthAndFirst - (1 << 160);
            }
        }
    }
    /// @dev Set element for storage `self` at `index` to `account`.
    function set(
        Data storage self,
        uint256 index,
        address account
    ) internal {
        uint256 len = length(self);
        if (index >= len) revert IndexOutOfBounds();
        if (index == 0) {
            self._raw[0] = (len << 160) | uint160(account);
        } else {
            self._raw[index] = uint160(account);
        }
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
import "./AddressArray.sol";
/** @title Library that is using AddressArray library for AddressArray.Data
 * and allows Set operations on address storage data:
 * 1. add
 * 2. remove
 * 3. contains
 */
library AddressSet {
    using AddressArray for AddressArray.Data;
    /** @dev Data struct from AddressArray.Data items
     * and lookup mapping address => index in data array.
     */
    struct Data {
        AddressArray.Data items;
        mapping(address => uint256) lookup;
    }
    /// @dev Length of data storage.
    function length(Data storage s) internal view returns (uint256) {
        return s.items.length();
    }
    /// @dev Returns data item from `s` storage at `index`.
    function at(Data storage s, uint256 index) internal view returns (address) {
        return s.items.at(index);
    }
    /// @dev Returns true if storage `s` has `item`.
    function contains(Data storage s, address item) internal view returns (bool) {
        return s.lookup[item] != 0;
    }
    /// @dev Adds `item` into storage `s` and returns true if successful.
    function add(Data storage s, address item) internal returns (bool) {
        if (s.lookup[item] > 0) {
            return false;
        }
        s.lookup[item] = s.items.push(item);
        return true;
    }
    /// @dev Removes `item` from storage `s` and returns true if successful.
    function remove(Data storage s, address item) internal returns (bool) {
        uint256 index = s.lookup[item];
        if (index == 0) {
            return false;
        }
        if (index < s.items.length()) {
            unchecked {
                address lastItem = s.items.at(s.items.length() - 1);
                s.items.set(index - 1, lastItem);
                s.lookup[lastItem] = index;
            }
        }
        s.items.pop();
        delete s.lookup[item];
        return true;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
/// @title Revert reason forwarder.
library RevertReasonForwarder {
    /// @dev Forwards latest externall call revert.
    function reRevert() internal pure {
        // bubble up revert reason from latest external call
        /// @solidity memory-safe-assembly
        assembly { // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)
            returndatacopy(ptr, 0, returndatasize())
            revert(ptr, returndatasize())
        }
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "../libraries/RevertReasonForwarder.sol";
/// @title Implements efficient safe methods for ERC20 interface.
library SafeERC20 {
    error SafeTransferFailed();
    error SafeTransferFromFailed();
    error ForceApproveFailed();
    error SafeIncreaseAllowanceFailed();
    error SafeDecreaseAllowanceFailed();
    error SafePermitBadLength();
    /// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bytes4 selector = token.transferFrom.selector;
        bool success;
        /// @solidity memory-safe-assembly
        assembly { // solhint-disable-line no-inline-assembly
            let data := mload(0x40)
            mstore(data, selector)
            mstore(add(data, 0x04), from)
            mstore(add(data, 0x24), to)
            mstore(add(data, 0x44), amount)
            success := call(gas(), token, 0, data, 100, 0x0, 0x20)
            if success {
                switch returndatasize()
                case 0 {
                    success := gt(extcodesize(token), 0)
                }
                default {
                    success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                }
            }
        }
        if (!success) revert SafeTransferFromFailed();
    }
    /// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        if (!_makeCall(token, token.transfer.selector, to, value)) {
            revert SafeTransferFailed();
        }
    }
    /// @dev If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry.
    function forceApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        if (!_makeCall(token, token.approve.selector, spender, value)) {
            if (
                !_makeCall(token, token.approve.selector, spender, 0) ||
                !_makeCall(token, token.approve.selector, spender, value)
            ) {
                revert ForceApproveFailed();
            }
        }
    }
    /// @dev Allowance increase with safe math check.
    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
        forceApprove(token, spender, allowance + value);
    }
    /// @dev Allowance decrease with safe math check.
    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (value > allowance) revert SafeDecreaseAllowanceFailed();
        forceApprove(token, spender, allowance - value);
    }
    /// @dev Calls either ERC20 or Dai `permit` for `token`, if unsuccessful forwards revert from external call.
    function safePermit(IERC20 token, bytes calldata permit) internal {
        if (!tryPermit(token, permit)) RevertReasonForwarder.reRevert();
    }
    function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool) {
        if (permit.length == 32 * 7) {
            return _makeCalldataCall(token, IERC20Permit.permit.selector, permit);
        }
        if (permit.length == 32 * 8) {
            return _makeCalldataCall(token, IDaiLikePermit.permit.selector, permit);
        }
        revert SafePermitBadLength();
    }
    function _makeCall(
        IERC20 token,
        bytes4 selector,
        address to,
        uint256 amount
    ) private returns (bool success) {
        /// @solidity memory-safe-assembly
        assembly { // solhint-disable-line no-inline-assembly
            let data := mload(0x40)
            mstore(data, selector)
            mstore(add(data, 0x04), to)
            mstore(add(data, 0x24), amount)
            success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
            if success {
                switch returndatasize()
                case 0 {
                    success := gt(extcodesize(token), 0)
                }
                default {
                    success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                }
            }
        }
    }
    function _makeCalldataCall(
        IERC20 token,
        bytes4 selector,
        bytes calldata args
    ) private returns (bool success) {
        /// @solidity memory-safe-assembly
        assembly { // solhint-disable-line no-inline-assembly
            let len := add(4, args.length)
            let data := mload(0x40)
            mstore(data, selector)
            calldatacopy(add(data, 0x04), args.offset, args.length)
            success := call(gas(), token, 0, data, len, 0x0, 0x20)
            if success {
                switch returndatasize()
                case 0 {
                    success := gt(extcodesize(token), 0)
                }
                default {
                    success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                }
            }
        }
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }
    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        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 (last updated v4.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;
    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }
    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }
    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }
    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }
    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }
    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }
    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }
    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }
    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }
    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }
    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }
    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }
        return true;
    }
    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        _beforeTokenTransfer(from, to, amount);
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }
        emit Transfer(from, to, amount);
        _afterTokenTransfer(from, to, amount);
    }
    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");
        _beforeTokenTransfer(address(0), account, amount);
        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);
        _afterTokenTransfer(address(0), account, amount);
    }
    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");
        _beforeTokenTransfer(account, address(0), amount);
        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }
        emit Transfer(account, address(0), amount);
        _afterTokenTransfer(account, address(0), amount);
    }
    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }
    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);
    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);
    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);
    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);
    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.
        return account.code.length > 0;
    }
    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");
        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }
    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }
    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }
    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-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;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }
    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }
    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }
    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }
            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }
            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);
            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////
            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)
                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }
            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)
                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)
                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }
            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;
            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;
            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256
            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }
    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }
    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);
        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }
    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
contract VotingPowerCalculator {
    error OriginInTheFuture();
    uint256 private constant _ONE = 1e18;
    uint256 public immutable origin;
    uint256 public immutable expBase;
    uint256 private immutable _expTable0;
    uint256 private immutable _expTable1;
    uint256 private immutable _expTable2;
    uint256 private immutable _expTable3;
    uint256 private immutable _expTable4;
    uint256 private immutable _expTable5;
    uint256 private immutable _expTable6;
    uint256 private immutable _expTable7;
    uint256 private immutable _expTable8;
    uint256 private immutable _expTable9;
    uint256 private immutable _expTable10;
    uint256 private immutable _expTable11;
    uint256 private immutable _expTable12;
    uint256 private immutable _expTable13;
    uint256 private immutable _expTable14;
    uint256 private immutable _expTable15;
    uint256 private immutable _expTable16;
    uint256 private immutable _expTable17;
    uint256 private immutable _expTable18;
    uint256 private immutable _expTable19;
    uint256 private immutable _expTable20;
    uint256 private immutable _expTable21;
    uint256 private immutable _expTable22;
    uint256 private immutable _expTable23;
    uint256 private immutable _expTable24;
    uint256 private immutable _expTable25;
    uint256 private immutable _expTable26;
    uint256 private immutable _expTable27;
    uint256 private immutable _expTable28;
    uint256 private immutable _expTable29;
    constructor(uint256 expBase_, uint256 origin_) {
        if (origin_ > block.timestamp) revert OriginInTheFuture();
        origin = origin_;
        expBase = expBase_;
        _expTable0 = expBase_;
        _expTable1 = (_expTable0 * _expTable0) / _ONE;
        _expTable2 = (_expTable1 * _expTable1) / _ONE;
        _expTable3 = (_expTable2 * _expTable2) / _ONE;
        _expTable4 = (_expTable3 * _expTable3) / _ONE;
        _expTable5 = (_expTable4 * _expTable4) / _ONE;
        _expTable6 = (_expTable5 * _expTable5) / _ONE;
        _expTable7 = (_expTable6 * _expTable6) / _ONE;
        _expTable8 = (_expTable7 * _expTable7) / _ONE;
        _expTable9 = (_expTable8 * _expTable8) / _ONE;
        _expTable10 = (_expTable9 * _expTable9) / _ONE;
        _expTable11 = (_expTable10 * _expTable10) / _ONE;
        _expTable12 = (_expTable11 * _expTable11) / _ONE;
        _expTable13 = (_expTable12 * _expTable12) / _ONE;
        _expTable14 = (_expTable13 * _expTable13) / _ONE;
        _expTable15 = (_expTable14 * _expTable14) / _ONE;
        _expTable16 = (_expTable15 * _expTable15) / _ONE;
        _expTable17 = (_expTable16 * _expTable16) / _ONE;
        _expTable18 = (_expTable17 * _expTable17) / _ONE;
        _expTable19 = (_expTable18 * _expTable18) / _ONE;
        _expTable20 = (_expTable19 * _expTable19) / _ONE;
        _expTable21 = (_expTable20 * _expTable20) / _ONE;
        _expTable22 = (_expTable21 * _expTable21) / _ONE;
        _expTable23 = (_expTable22 * _expTable22) / _ONE;
        _expTable24 = (_expTable23 * _expTable23) / _ONE;
        _expTable25 = (_expTable24 * _expTable24) / _ONE;
        _expTable26 = (_expTable25 * _expTable25) / _ONE;
        _expTable27 = (_expTable26 * _expTable26) / _ONE;
        _expTable28 = (_expTable27 * _expTable27) / _ONE;
        _expTable29 = (_expTable28 * _expTable28) / _ONE;
    }
    function _votingPowerAt(uint256 balance, uint256 timestamp) internal view returns (uint256 votingPower) {
        timestamp = timestamp < origin ? origin : timestamp;  // logic in timestamps before origin is undefined
        unchecked {
            uint256 t = timestamp - origin;
            votingPower = balance;
            if (t & 0x01 != 0) {
                votingPower = (votingPower * _expTable0) / _ONE;
            }
            if (t & 0x02 != 0) {
                votingPower = (votingPower * _expTable1) / _ONE;
            }
            if (t & 0x04 != 0) {
                votingPower = (votingPower * _expTable2) / _ONE;
            }
            if (t & 0x08 != 0) {
                votingPower = (votingPower * _expTable3) / _ONE;
            }
            if (t & 0x10 != 0) {
                votingPower = (votingPower * _expTable4) / _ONE;
            }
            if (t & 0x20 != 0) {
                votingPower = (votingPower * _expTable5) / _ONE;
            }
            if (t & 0x40 != 0) {
                votingPower = (votingPower * _expTable6) / _ONE;
            }
            if (t & 0x80 != 0) {
                votingPower = (votingPower * _expTable7) / _ONE;
            }
            if (t & 0x100 != 0) {
                votingPower = (votingPower * _expTable8) / _ONE;
            }
            if (t & 0x200 != 0) {
                votingPower = (votingPower * _expTable9) / _ONE;
            }
            if (t & 0x400 != 0) {
                votingPower = (votingPower * _expTable10) / _ONE;
            }
            if (t & 0x800 != 0) {
                votingPower = (votingPower * _expTable11) / _ONE;
            }
            if (t & 0x1000 != 0) {
                votingPower = (votingPower * _expTable12) / _ONE;
            }
            if (t & 0x2000 != 0) {
                votingPower = (votingPower * _expTable13) / _ONE;
            }
            if (t & 0x4000 != 0) {
                votingPower = (votingPower * _expTable14) / _ONE;
            }
            if (t & 0x8000 != 0) {
                votingPower = (votingPower * _expTable15) / _ONE;
            }
            if (t & 0x10000 != 0) {
                votingPower = (votingPower * _expTable16) / _ONE;
            }
            if (t & 0x20000 != 0) {
                votingPower = (votingPower * _expTable17) / _ONE;
            }
            if (t & 0x40000 != 0) {
                votingPower = (votingPower * _expTable18) / _ONE;
            }
            if (t & 0x80000 != 0) {
                votingPower = (votingPower * _expTable19) / _ONE;
            }
            if (t & 0x100000 != 0) {
                votingPower = (votingPower * _expTable20) / _ONE;
            }
            if (t & 0x200000 != 0) {
                votingPower = (votingPower * _expTable21) / _ONE;
            }
            if (t & 0x400000 != 0) {
                votingPower = (votingPower * _expTable22) / _ONE;
            }
            if (t & 0x800000 != 0) {
                votingPower = (votingPower * _expTable23) / _ONE;
            }
            if (t & 0x1000000 != 0) {
                votingPower = (votingPower * _expTable24) / _ONE;
            }
            if (t & 0x2000000 != 0) {
                votingPower = (votingPower * _expTable25) / _ONE;
            }
            if (t & 0x4000000 != 0) {
                votingPower = (votingPower * _expTable26) / _ONE;
            }
            if (t & 0x8000000 != 0) {
                votingPower = (votingPower * _expTable27) / _ONE;
            }
            if (t & 0x10000000 != 0) {
                votingPower = (votingPower * _expTable28) / _ONE;
            }
            if (t & 0x20000000 != 0) {
                votingPower = (votingPower * _expTable29) / _ONE;
            }
        }
        return votingPower;
    }
    function _balanceAt(uint256 votingPower, uint256 timestamp) internal view returns (uint256 balance) {
        timestamp = timestamp < origin ? origin : timestamp;  // logic in timestamps before origin is undefined
        unchecked {
            uint256 t = timestamp - origin;
            balance = votingPower;
            if (t & 0x01 != 0) {
                balance = (balance * _ONE) / _expTable0;
            }
            if (t & 0x02 != 0) {
                balance = (balance * _ONE) / _expTable1;
            }
            if (t & 0x04 != 0) {
                balance = (balance * _ONE) / _expTable2;
            }
            if (t & 0x08 != 0) {
                balance = (balance * _ONE) / _expTable3;
            }
            if (t & 0x10 != 0) {
                balance = (balance * _ONE) / _expTable4;
            }
            if (t & 0x20 != 0) {
                balance = (balance * _ONE) / _expTable5;
            }
            if (t & 0x40 != 0) {
                balance = (balance * _ONE) / _expTable6;
            }
            if (t & 0x80 != 0) {
                balance = (balance * _ONE) / _expTable7;
            }
            if (t & 0x100 != 0) {
                balance = (balance * _ONE) / _expTable8;
            }
            if (t & 0x200 != 0) {
                balance = (balance * _ONE) / _expTable9;
            }
            if (t & 0x400 != 0) {
                balance = (balance * _ONE) / _expTable10;
            }
            if (t & 0x800 != 0) {
                balance = (balance * _ONE) / _expTable11;
            }
            if (t & 0x1000 != 0) {
                balance = (balance * _ONE) / _expTable12;
            }
            if (t & 0x2000 != 0) {
                balance = (balance * _ONE) / _expTable13;
            }
            if (t & 0x4000 != 0) {
                balance = (balance * _ONE) / _expTable14;
            }
            if (t & 0x8000 != 0) {
                balance = (balance * _ONE) / _expTable15;
            }
            if (t & 0x10000 != 0) {
                balance = (balance * _ONE) / _expTable16;
            }
            if (t & 0x20000 != 0) {
                balance = (balance * _ONE) / _expTable17;
            }
            if (t & 0x40000 != 0) {
                balance = (balance * _ONE) / _expTable18;
            }
            if (t & 0x80000 != 0) {
                balance = (balance * _ONE) / _expTable19;
            }
            if (t & 0x100000 != 0) {
                balance = (balance * _ONE) / _expTable20;
            }
            if (t & 0x200000 != 0) {
                balance = (balance * _ONE) / _expTable21;
            }
            if (t & 0x400000 != 0) {
                balance = (balance * _ONE) / _expTable22;
            }
            if (t & 0x800000 != 0) {
                balance = (balance * _ONE) / _expTable23;
            }
            if (t & 0x1000000 != 0) {
                balance = (balance * _ONE) / _expTable24;
            }
            if (t & 0x2000000 != 0) {
                balance = (balance * _ONE) / _expTable25;
            }
            if (t & 0x4000000 != 0) {
                balance = (balance * _ONE) / _expTable26;
            }
            if (t & 0x8000000 != 0) {
                balance = (balance * _ONE) / _expTable27;
            }
            if (t & 0x10000000 != 0) {
                balance = (balance * _ONE) / _expTable28;
            }
            if (t & 0x20000000 != 0) {
                balance = (balance * _ONE) / _expTable29;
            }
        }
        return balance;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IVotable is IERC20 {
    /// @dev we assume that voting power is a function of balance that preserves order
    function votingPowerOf(address account) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@1inch/erc20-pods/contracts/ERC20Pods.sol";
import "@1inch/erc20-pods/contracts/Pod.sol";
import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import "./helpers/VotingPowerCalculator.sol";
import "./interfaces/IVotable.sol";
/**
 * @title 1inch staking contract
 * @notice The contract provides the following features: staking, delegation, farming
 * How lock period works:
 * - balances and voting power
 * - Lock min and max
 * - Add lock
 * - earlyWithdrawal
 * - penalty math
 */
contract St1inch is ERC20Pods, Ownable, VotingPowerCalculator, IVotable {
    using SafeERC20 for IERC20;
    event EmergencyExitSet(bool status);
    event MaxLossRatioSet(uint256 ratio);
    event MinLockPeriodRatioSet(uint256 ratio);
    event FeeReceiverSet(address receiver);
    event DefaultFarmSet(address defaultFarm);
    error ApproveDisabled();
    error TransferDisabled();
    error LockTimeMoreMaxLock();
    error LockTimeLessMinLock();
    error UnlockTimeHasNotCome();
    error StakeUnlocked();
    error MinLockPeriodRatioNotReached();
    error MinReturnIsNotMet();
    error MaxLossIsNotMet();
    error MaxLossOverflow();
    error LossIsTooBig();
    error RescueAmountIsTooLarge();
    error ExpBaseTooBig();
    error ExpBaseTooSmall();
    error DefaultFarmTokenMismatch();
    error DepositsDisabled();
    error ZeroAddress();
    /// @notice The minimum allowed staking period
    uint256 public constant MIN_LOCK_PERIOD = 30 days;
    /// @notice The maximum allowed staking period
    /// @dev WARNING: It is not enough to change the constant only but voting power decrease curve should be revised also
    uint256 public constant MAX_LOCK_PERIOD = 2 * 365 days;
    /// @notice Voting power decreased to 1/_VOTING_POWER_DIVIDER after lock expires
    /// @dev WARNING: It is not enough to change the constant only but voting power decrease curve should be revised also
    uint256 private constant _VOTING_POWER_DIVIDER = 20;
    uint256 private constant _PODS_LIMIT = 5;
    /// @notice Maximum allowed gas spent by each attached pod. If there not enough gas for pod execution then
    /// transaction is reverted. If pod uses more gas then its execution is reverted silently, not affection the
    /// main transaction
    uint256 private constant _POD_CALL_GAS_LIMIT = 500_000;
    uint256 private constant _ONE = 1e9;
    IERC20 public immutable oneInch;
    /// @notice The stucture to store stake information for a staker
    struct Depositor {
        uint40 lockTime;    // Unix time in seconds
        uint40 unlockTime;  // Unix time in seconds
        uint176 amount;     // Staked 1inch token amount
    }
    mapping(address => Depositor) public depositors;
    uint256 public totalDeposits;
    bool public emergencyExit;
    uint256 public maxLossRatio;
    uint256 public minLockPeriodRatio;
    address public feeReceiver;
    address public defaultFarm;
    /**
     * @notice Initializes the contract
     * @param oneInch_ The token to be staked
     * @param expBase_ The rate for the voting power decrease over time
     */
    constructor(IERC20 oneInch_, uint256 expBase_)
        ERC20Pods(_PODS_LIMIT, _POD_CALL_GAS_LIMIT)
        ERC20("Staking 1INCH v2", "st1INCH")
        VotingPowerCalculator(expBase_, block.timestamp)
    {
        // voting power after MAX_LOCK_PERIOD should be equal to staked amount divided by _VOTING_POWER_DIVIDER
        if (_votingPowerAt(1e18, block.timestamp + MAX_LOCK_PERIOD) * _VOTING_POWER_DIVIDER < 1e18) revert ExpBaseTooBig();
        if (_votingPowerAt(1e18, block.timestamp + MAX_LOCK_PERIOD + 1) * _VOTING_POWER_DIVIDER > 1e18) revert ExpBaseTooSmall();
        oneInch = oneInch_;
    }
    /**
     * @notice Sets the new contract that would recieve early withdrawal fees
     * @param feeReceiver_ The receiver contract address
     */
    function setFeeReceiver(address feeReceiver_) external onlyOwner {
        if (feeReceiver_ == address(0)) revert ZeroAddress();
        feeReceiver = feeReceiver_;
        emit FeeReceiverSet(feeReceiver_);
    }
    /**
     * @notice Sets the new farm that all staking users will automatically join after staking for reward farming
     * @param defaultFarm_ The farm contract address
     */
    function setDefaultFarm(address defaultFarm_) external onlyOwner {
        if (defaultFarm_ != address(0) && Pod(defaultFarm_).token() != this) revert DefaultFarmTokenMismatch();
        defaultFarm = defaultFarm_;
        emit DefaultFarmSet(defaultFarm_);
    }
    /**
     * @notice Sets the maximum allowed loss ratio for early withdrawal. If the ratio is not met, actual is more than allowed,
     * then early withdrawal will revert.
     * Example: maxLossRatio = 90% and 1000 staked 1inch tokens means that a user can execute early withdrawal only
     * if his loss is less than or equals 90% of his stake, which is 900 tokens. Thus, if a user loses 900 tokens he is allowed
     * to do early withdrawal and not if the loss is greater.
     * @param maxLossRatio_ The maximum loss allowed (9 decimals).
     */
    function setMaxLossRatio(uint256 maxLossRatio_) external onlyOwner {
        if (maxLossRatio_ > _ONE) revert MaxLossOverflow();
        maxLossRatio = maxLossRatio_;
        emit MaxLossRatioSet(maxLossRatio_);
    }
    /**
     * @notice Sets the minimum allowed lock period ratio for early withdrawal. If the ratio is not met, actual is more than allowed,
     * then early withdrawal will revert.
     * @param minLockPeriodRatio_ The maximum loss allowed (9 decimals).
     */
    function setMinLockPeriodRatio(uint256 minLockPeriodRatio_) external onlyOwner {
        if (minLockPeriodRatio_ > _ONE) revert MaxLossOverflow();
        minLockPeriodRatio = minLockPeriodRatio_;
        emit MinLockPeriodRatioSet(minLockPeriodRatio_);
    }
    /**
     * @notice Sets the emergency exit mode. In emergency mode any stake may withdraw its stake regardless of lock.
     * The mode is intended to use only for migration to a new version of staking contract.
     * @param emergencyExit_ set `true` to enter emergency exit mode and `false` to return to normal operations
     */
    function setEmergencyExit(bool emergencyExit_) external onlyOwner {
        emergencyExit = emergencyExit_;
        emit EmergencyExitSet(emergencyExit_);
    }
    /**
     * @notice Gets the voting power of the provided account
     * @param account The address of an account to get voting power for
     * @return votingPower The voting power available at the block timestamp
     */
    function votingPowerOf(address account) external view returns (uint256) {
        return _votingPowerAt(balanceOf(account), block.timestamp);
    }
    /**
     * @notice Gets the voting power of the provided account at the given timestamp
     * @dev To calculate voting power at any timestamp provided the contract stores each balance
     * as it was staked for the maximum lock time. If a staker locks its stake for less than the maximum
     * then at the moment of deposit its balance is recorded as it was staked for the maximum but time
     * equal to `max lock period-lock time` has passed. It makes available voting power calculation
     * available at any point in time within the maximum lock period.
     * @param account The address of an account to get voting power for
     * @param timestamp The timestamp to calculate voting power at
     * @return votingPower The voting power available at the moment of `timestamp`
     */
    function votingPowerOfAt(address account, uint256 timestamp) external view returns (uint256) {
        return _votingPowerAt(balanceOf(account), timestamp);
    }
    /**
     * @notice Gets the voting power for the provided balance at the current timestamp assuming that
     * the balance is a balance at the moment of the maximum lock time
     * @param balance The balance for the maximum lock time
     * @return votingPower The voting power available at the block timestamp
     */
    function votingPower(uint256 balance) external view returns (uint256) {
        return _votingPowerAt(balance, block.timestamp);
    }
    /**
     * @notice Gets the voting power for the provided balance at the current timestamp assuming that
     * the balance is a balance at the moment of the maximum lock time
     * @param balance The balance for the maximum lock time
     * @param timestamp The timestamp to calculate the voting power at
     * @return votingPower The voting power available at the block timestamp
     */
    function votingPowerAt(uint256 balance, uint256 timestamp) external view returns (uint256) {
        return _votingPowerAt(balance, timestamp);
    }
    /**
     * @notice Stakes given amount and locks it for the given duration
     * @param amount The amount of tokens to stake
     * @param duration The lock period in seconds. If there is a stake locked then the lock period is extended by the duration.
     * To keep the current lock period unchanged pass 0 for the duration.
     */
    function deposit(uint256 amount, uint256 duration) external {
        _deposit(msg.sender, amount, duration);
    }
    /**
     * @notice Stakes given amount and locks it for the given duration with permit
     * @param amount The amount of tokens to stake
     * @param duration The lock period in seconds. If there is a stake locked then the lock period is extended by the duration.
     * To keep the current lock period unchanged pass 0 for the duration
     * @param permit Permit given by the staker
     */
    function depositWithPermit(uint256 amount, uint256 duration, bytes calldata permit) external {
        oneInch.safePermit(permit);
        _deposit(msg.sender, amount, duration);
    }
    /**
     * @notice Stakes given amount on behalf of provided account without locking or extending lock
     * @param account The account to stake for
     * @param amount The amount to stake
     */
    function depositFor(address account, uint256 amount) external {
        _deposit(account, amount, 0);
    }
    /**
     * @notice Stakes given amount on behalf of provided account without locking or extending lock with permit
     * @param account The account to stake for
     * @param amount The amount to stake
     * @param permit Permit given by the caller
     */
    function depositForWithPermit(address account, uint256 amount, bytes calldata permit) external {
        oneInch.safePermit(permit);
        _deposit(account, amount, 0);
    }
    function _deposit(address account, uint256 amount, uint256 duration) private {
        if (emergencyExit) revert DepositsDisabled();
        Depositor memory depositor = depositors[account]; // SLOAD
        uint256 lockedTill = Math.max(depositor.unlockTime, block.timestamp) + duration;
        uint256 lockLeft = lockedTill - block.timestamp;
        if (lockLeft < MIN_LOCK_PERIOD) revert LockTimeLessMinLock();
        if (lockLeft > MAX_LOCK_PERIOD) revert LockTimeMoreMaxLock();
        uint256 balanceDiff = _balanceAt(depositor.amount + amount, lockedTill) / _VOTING_POWER_DIVIDER - balanceOf(account);
        depositor.lockTime = uint40(duration == 0 ? depositor.lockTime : block.timestamp);
        depositor.unlockTime = uint40(lockedTill);
        depositor.amount += uint176(amount);
        depositors[account] = depositor; // SSTORE
        totalDeposits += amount;
        _mint(account, balanceDiff);
        if (amount > 0) {
            oneInch.safeTransferFrom(msg.sender, address(this), amount);
        }
        if (defaultFarm != address(0) && !hasPod(account, defaultFarm)) {
            _addPod(account, defaultFarm);
        }
    }
    /**
     * @notice Withdraw stake before lock period expires at the cost of losing part of a stake.
     * The stake loss is proportional to the time passed from the maximum lock period to the lock expiration and voting power.
     * The more time is passed the less would be the loss.
     * Formula to calculate return amount = (deposit - voting power)) / 0.95
     * @param minReturn The minumum amount of stake acceptable for return. If actual amount is less then the transaction is reverted
     * @param maxLoss The maximum amount of loss acceptable. If actual loss is bigger then the transaction is reverted
     */
    function earlyWithdraw(uint256 minReturn, uint256 maxLoss) external {
        earlyWithdrawTo(msg.sender, minReturn, maxLoss);
    }
    /**
     * @notice Withdraw stake before lock period expires at the cost of losing part of a stake to the specified account
     * The stake loss is proportional to the time passed from the maximum lock period to the lock expiration and voting power.
     * The more time is passed the less would be the loss.
     * Formula to calculate return amount = (deposit - voting power)) / 0.95
     * @param to The account to withdraw the stake to
     * @param minReturn The minumum amount of stake acceptable for return. If actual amount is less then the transaction is reverted
     * @param maxLoss The maximum amount of loss acceptable. If actual loss is bigger then the transaction is reverted
     */
    // ret(balance) = (deposit - vp(balance)) / 0.95
    function earlyWithdrawTo(address to, uint256 minReturn, uint256 maxLoss) public {
        Depositor memory depositor = depositors[msg.sender]; // SLOAD
        if (emergencyExit || block.timestamp >= depositor.unlockTime) revert StakeUnlocked();
        uint256 allowedExitTime = depositor.lockTime + (depositor.unlockTime - depositor.lockTime) * minLockPeriodRatio / _ONE;
        if (block.timestamp < allowedExitTime) revert MinLockPeriodRatioNotReached();
        uint256 amount = depositor.amount;
        if (amount > 0) {
            uint256 balance = balanceOf(msg.sender);
            (uint256 loss, uint256 ret) = _earlyWithdrawLoss(amount, balance);
            if (ret < minReturn) revert MinReturnIsNotMet();
            if (loss > maxLoss) revert MaxLossIsNotMet();
            if (loss > amount * maxLossRatio / _ONE) revert LossIsTooBig();
            _withdraw(depositor, balance);
            oneInch.safeTransfer(to, ret);
            oneInch.safeTransfer(feeReceiver, loss);
        }
    }
    /**
     * @notice Gets the loss amount if the staker do early withdrawal at the current block
     * @param account The account to calculate early withdrawal loss for
     * @return loss The loss amount amount
     * @return ret The return amount
     * @return canWithdraw  True if the staker can withdraw without penalty, false otherwise
     */
    function earlyWithdrawLoss(address account) external view returns (uint256 loss, uint256 ret, bool canWithdraw) {
        uint256 amount = depositors[account].amount;
        (loss, ret) = _earlyWithdrawLoss(amount, balanceOf(account));
        canWithdraw = loss <= amount * maxLossRatio / _ONE;
    }
    function _earlyWithdrawLoss(uint256 depAmount, uint256 stBalance) private view returns (uint256 loss, uint256 ret) {
        ret = (depAmount - _votingPowerAt(stBalance, block.timestamp)) * 100 / 95;
        loss = depAmount - ret;
    }
    /**
     * @notice Withdraws stake if lock period expired
     */
    function withdraw() external {
        withdrawTo(msg.sender);
    }
    /**
     * @notice Withdraws stake if lock period expired to the given address
     */
    function withdrawTo(address to) public {
        Depositor memory depositor = depositors[msg.sender]; // SLOAD
        if (!emergencyExit && block.timestamp < depositor.unlockTime) revert UnlockTimeHasNotCome();
        uint256 amount = depositor.amount;
        if (amount > 0) {
            _withdraw(depositor, balanceOf(msg.sender));
            oneInch.safeTransfer(to, amount);
        }
    }
    function _withdraw(Depositor memory depositor, uint256 balance) private {
        totalDeposits -= depositor.amount;
        depositor.amount = 0;
        // keep unlockTime in storage for next tx optimization
        depositor.unlockTime = uint40(Math.min(depositor.unlockTime, block.timestamp));
        depositors[msg.sender] = depositor; // SSTORE
        _burn(msg.sender, balance);
    }
    /**
     * @notice Retrieves funds from the contract in emergency situations
     * @param token The token to retrieve
     * @param amount The amount of funds to transfer
     */
    function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
        if (address(token) == address(0)) {
            Address.sendValue(payable(msg.sender), amount);
        } else {
            if (token == oneInch) {
                if (amount > oneInch.balanceOf(address(this)) - totalDeposits) revert RescueAmountIsTooLarge();
            }
            token.safeTransfer(msg.sender, amount);
        }
    }
    // ERC20 methods disablers
    function approve(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
        revert ApproveDisabled();
    }
    function transfer(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
        revert TransferDisabled();
    }
    function transferFrom(address, address, uint256) public pure override(IERC20, ERC20) returns (bool) {
        revert TransferDisabled();
    }
    function increaseAllowance(address, uint256) public pure override returns (bool) {
        revert ApproveDisabled();
    }
    function decreaseAllowance(address, uint256) public pure override returns (bool) {
        revert ApproveDisabled();
    }
}