ETH Price: $3,797.11 (+6.61%)

Transaction Decoder

Block:
16781811 at Mar-08-2023 06:39:11 AM +UTC
Transaction Fee:
0.08802416947159248 ETH $334.24
Gas Used:
4,170,103 Gas / 21.10839216 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
(Abyss Finance: Eth2 Depositor 217)
24.040229316518298913 Eth24.041217648431221204 Eth0.000988331912922291
0x97471c0f...d3a53F38f
0.126927504420292227 Eth
Nonce: 14
0.038903334948699747 Eth
Nonce: 15
0.08802416947159248
0x9D1A92e6...4bCCA3128
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 298943282377461497914187525585960442251570656537235082103041247055466572774173570079428295929603184810712225293328755702173458400521949749605801750844570345105177911518759761426758381566154644947358204605304390170585230288709926262559820381193604241478376723023244547664892251297727107607477503399029832700925409334219065424775350417819949886701749228501930095763759328869577195722030594895912009723576823493341176907107914112884562746609587267560154326117199576447992856881179801315260688028818904921292391311998740014894404233267877161969232168602270611545935855671689728046747110933243655931736076085766771912334427847051441362630049957902147461756831626151812417213394263631265313856955471560457509091626331337788824195890210113031242696476681146211144703338204321169080039579418196536773422059728240184152726518004873466640101902336968216485386662117164123725041515153740821934289250119259737115714361899822387558390620935087662369085984193555030046238005934255543769666422417957741087299780078713814578760116292488885239449997838695336072332759374942289327905892523015074354136836433983194985314472385320342683862818910173071814182316399682174458228464030503579597599395402486150622845920900694940925410966958733849368065622837092742505166355916177687546226850339042201794304595441103288190435533277368163374499001351267843207448026201924395894521801834840915747412745025047618920518883118962886228634814333838874077574622394393393879316500303762305081324781182176533737208592140355716511370319387987888109663694127198229431726948467251404557558356462941970045449398277056963396004602562478015188027473835808697631264121422212422465621598710572332200917814938895887290429482292826615396942298198166258533492248020296697188465451155597565929360494579802828179516132012614378564357326147470502537204067502282273860435512820839375121997562695509888432101547210341177023413887357282559390225358725852783063470975947111246997252780958756010130588780427000977112779784026582935673907568075514101355339196567215123134144204827794464262409033638273846569459519128327887187948929158559465519087947876958649141267447506915344375953564913137349020700802557311214105757818503609276206762682548383883641101879373903313497958434691539397612695234517781247302118575476092853181502704756349533825553519832258185657806551638726551399922909186567778655367234960022373379016235349461050629276265682603362961103450518422755667321741674295439051987576515288412108176342336345698795632589675728091594099023947825180565202007184701239042969291370076236023139330030553383691227042950230120690671197483747921639931212607328118537013266056051632793919330575751703577079846336784237051312641067225542536924619079044960296121691240889525491642108774925099188741803321447288270405299588653153758108367024241649552878739307115839860722741744511273412777614276683692305861226862003748611616044463012792136135254159942385322978900245045062206546514415050330251755480070200876920709446643068394492798123655475389802828220320705406422736174407766528326030487439030390447424360879738418990706988364287836545627251851041776597048120788090473080835368359089642079501533737095830497573014996723464922352313281136881929938939599862463025711659248199429075014990235731590817806103229752524951741382297569354827807967757274984787613754094954507039365626806387824794899297231355546128430900669859820841056143494040887410186411003774765647275041663572172717777753267320757274352006510324765693091823206842009670823577171544101148582499608795405894626220987468544565774070803821089818134550839412614919692862581213843969256760025484256433678248332613352686322171013320583076133510599421820226437774558200143746255796463545643176084896469009553654071077370117168495109826144459175162075499260521888704178006031683667521292315155670890004404424424685471270032667316544891231825189977681283254418565016290476493416063071653800218469646466918063428053396327198180248363268946428147703792637594860697010540648276758292442983180713298981437783876179555289354230845847825054452876025217043929597908626250994850561124464955474533094360536298226207087976013900335983378843674304878065808021035752331578867539092168439490983954141126135344306277965104189943513338255256121792523895200009504484463547949604899389250654692231998101263297529878528212590180971666507097072808878087080310316807047220363914076346248946247123360839281939737066945429386071817715880402929208834567369273621792125235722868158529606248240447844620087756463436123235209286518663897233608702811033764474735293145201901015435263744339966263424145649454648454537458412380520308813556858643314686764384471684902134174278391314059220451022665742100178522788870150848104740381487886315666859206938642590641318038225393300186174851371383459991624338081245241370417422666632123768700976426978039494251988032676232773300172287844446141068920851549962503121893308465707275198091653376046570851733635608775644259076429971175628828517488291124779783855116299531187285505449085310261308608296599581259224235763654783384140604092957579493163748900704399142695666462285216417872789907983087222740995989921633006306481332626400651428475527492455823452126991919752758378560056878224468317314390602820579837200294149603111201999929675539604470889782911845441603281318282469149506937384101388428122052138376510099406488207970092903434562995584106264759620555908760439811825261256414232583621925110788906655704859505866848923831820494878726520688959954508578587254430240240109300430296103277120076424088587130644051942674050821122172520896249072327538614786455055928384512433958499912963911339537570602899628697690335600429622851538838097735168915388048760644593937959759697013913629898081231018797888312332515616170692134332053711067662789926733588796743768629001108567753289868587679168440336833761230741126521139837351965102179406222369520676772285296553432233031680530540009118829147689863998402055314403123162254499380757658486375417921591512546205605271784181235983370105551020981636884436854509105761343020923494833140441895724276043971876347275670612331497055105165993798807531976747653218709043947533778235132584492921893280139339229233203429440767454833925028137534312609027619762377703827046241754380790604129793012217893589310662027710732039034497149092967696979309098503169277199941917500507066375445059074024829972586615150066208128735547740632877168284034308070940939660830766088048775572335700902942227097219246532306566184777382660354207440349449771333916376951990805067732959115186618592828215036650052146938580164664433350656057398624807929497321802917656892445879955016273303778830415340176798615153923150127004850226641822400844172740594282249746415611271422144334343682957914524464194980106023513624564110011174371128282875832181223925815658712257247187069183960146845292655862721988880493073397022448179365411664574332538796733935953574148007388455069716550835388514685040395761232666325576513211834634403478236962201567227090131387205159963078370387905708975339497487714089700933721908090494699028434607442081527773494194349310873386098100291632835088492783045323166048307458949541471661604265056072977124789340988724383721197776033452507610137925293792004283950096230115248369149203218551206675434141581762491856580388071052533701337126308892198481318439267949652466545748876226264180477756205127191300487129017817996595773525934616802136595933398390733914687885136722505259637734511105652891533662205030026025583917327928178732533478775448765303615397010960519794997216397795796291649015625848946453410999577348379158733197011574479170210714129294928840673144559944901109619407723949085753092675331849797327307223774907667017876966710178351569044756811866428067334843499764297634015830573588113198207225748970177772831742876301396259081445911280859528166417007247932597911001060819690152631645154688679548813385328111150132006381009789354779014680495163869494653736048746365389491437159981014334114795178725848221399994226411792069666787313272408717428042373110454576479804209695052244961183663384487067815845080458398657473863879703688112727639243920833258057760271254077709398610049718323841585254212089575560284105592182776450673908573039158340008184651848639965252460563886037718400049075727591353958732693668743466196305121067236706563737033916500586193478448610230205966953603120644403175038944228026694456122944281312040357128871896898766551690738309322485848370907070044033497817194258382296312777288090120378369734475608218984732028250209986734896433564021699087945243305389879405909183258661749180279382459922390879254613205841244789535801867232250007977106615663014231581692076668309813091645045444093696201950746770775033789411754973473053015654394633666244414027299046130652058798271240068791685983977419999234367019577975250119087093315153742844071794330227019626387910889194281262236838959098317133448611981503352941689872741808018188443725364536150933961056954251282017847829610768957050619108965013974170505534003377503486999742068742717705803638779604134657600912714555355323574496483825232893008938969842277690603188881358978874897842547026292066941880390494771086656563352111638102850743060375267961878774014766369662929397351476967730947220739788267935895346167147487529208613377508487180159532330242070146048352917591078817519323489255605429822107905338118834439058546193141628606096750262743623750937421939023473472173226582583457609038039965897401026522378879925878456733297315907600726782053629869793889925478234768219241792969786766766790175915495748988358221623009591536525465907252317056176282125996524260806935123106086968306294836884543774354398092295170433725967198176363457695698066567718275923112857201023627626688804248946369389557434752050535940879214279217581133859738877593986303665515988459737215197323718639667547439958490818595354285898556888102915296023594070588277169895946345231792371366454035312726343779841209919797766240366815935457157872593971480161366814256085009095514649663283299060474436555504719597835716014157386054436239334797150771138770355951129713779328513828544162141532469223496283737859338564947449824000291874408235942819097402582426963599670666855277618762374369962478779553078420280026391152224024032419115082562270888911871528678366171168265965391226331340399968697085359744075641622548217559094287719363392600228857292240861882353259180256542639738450172618859542511058587433036186154760891337594524660350595767142935984138941404365332985031309296524899994721371772756942190359860521696129405709130881729998957517733862829996407077723771406343364549111372739262497090120666143725922756551952196359757853091208670651514799960709389651177833121862549689479078236478616395525186286309056665315717079017505786774660020329879111127956732555722709792276299239174418920973946365528440437137638089641109284737576861620309885676379074048027702137224271454335754407360852523211965340481860675877183937276115820968324497438175675361821812443740661611761339885203154366047509447518867024389344336967864537434034331069632839974853206872312893705591195985715506098474119631569556978111677233487579164204906457472276380880955166568278527360182524574364733403735227178871855317616162015414504191479121226356943583144945777618469340522215380276676094651365552130437460057819709771361436968737067583713168181162602850335590586001418764990350466635517897296738328579110177505673848864156463860892422466652282965903700719931857387441655854294352762636032059531608867096017868488743820079907068295269903134503922072846248801945758317928220563284043151902235762327476587986641109917658133011570828368032561789895261151494806852527395642244948651394358786051490014880794120253613910358037622063349114570172589141129662131997110099344943854702090215360851321971615774296739051713504442558514463600244217227471666833204081629588897357869444630740204958831793150146680579476955253865834736581820285234993436054977475430278445337988579024502963403697951768798956355309347176999947000359881229208872595420742665653605380206301844592137606823796131023123512475209846491838431373718701968681741001481552178260363261903667279652279238406169036363972544309137870592131526508000844257722976813316922078896096815550300842130753758223409833937363375757643568330218926926468490529200506775659494863086316364728181996929181785671490911747711418488323285944094642336800588299140007730130670432867384386919989516915648146078973047016036519161262964137523825454481236535568643715290224477440086170923982977437819575217260022670779533888703703588553372931171022588733676122556874008159988024945193781333195212019122442876137270821559099453043515963067594539105150913391598546368032242207750752026913441999973268765455429450427554659273215731100314675067297481019951027867854150476646121687835802122296813818796393105633580146788466109240593948503175798253193605645825359318560735619058049168980668505453354901067088095945099732049900947681005899851085168395694664149254321705752199191763469411168625433244604756927432884026541415699643058724923697870646759110860053342167653920528608997347102550994948364823948499988381594992582954968676501879127655409334738220826685134162675285974128853588935369235623431119897693157542337910476177426017551048999579935605212269131085391629066261519255701467396994156108409813959783189441315311860442671003783925500532157356221969330039210725002819143307321543889017483050922700399092270956519711913709627634030044800601239310876662818483079571766611235228180812166824163912224012326760473912265210198564390206969435475559181585157079855672817136034045393715512449297693319600954052575467178128540366744854225347427009793036478440857965823908145411250204955894439872489968281336147040824170627057573532002755975729879440044142670135108543386600360985781985553452209887689334274869954983883782971928530470939164872084784794305181188785979375207312738109282668292739485635118546003125555981608581656175766690003711362499817830443488475081552839618551459466796450312036547899454064023102424285444220314494013457214936092372021660581651158053633365101327370117164669383597432382377442844382575828882988563010799559214655788038412356062338292017222936873453581192480981787782214368432019359566648329750905029633319500257541467886588092181120559153099447225001021368507153415454266551365891880473504640891036526715299719684177803060922003605451565502189249602624917376397906479929623886008768855790943143369907058618924348029352714312521524320024204444284012244178033186454634289577847631871582485346194658674358509049861312388650121573744896829546170043786635145514542856405414320491942454651933339286143511868346396788194253024267236976530372194108700231713718094834673021968800305002220127974304348516328003611283401737715057456654225970480923795949786263909505252235883818313032576075262953366859163256389302963482452548175576712032083198316242851315304465765674398214904336615303527783770193381917988803513053041966822018942692791709924833482156015105285253376005006031437801800567177838986624756103504957769686020646814633182412576510808763796763335993127361192099243929396461044991068519392413887244846129671806572071711731764164715297782476823794668474888386038815771481566237166337454533165822300120024525433455304313146403916012571594161932788258017490257092855915667406455864147308559853760216547810837778304927438592700278007252096377477985440267994268945781323944364855550121387024321283374563579371136942095375841168338078860868942869351756563530600919368715646443939001211810031511133068239435495441721177064058117355484294755766152559528180200087108582417762298023710770439680604124137711149036175319557371455040957075295437072157131849095422713671776935856767436021946141958504401306467215089126831909571579810703804914447113727792607520583522520215362093706397621727400693115074833802666652533130442524773491034567262540958445913882372095629853761796122387148059917704272940440663152108647146815217790940109133536531281719811991006396431241523157925156387989048307730219958106894338311942328952335878362903612271132444174165681816255939425855601668930403820219334687584029332990482813827443490333902760081834577555732883186293098563466102750532526321240185593176719994701974192711617101513820893084734018581170075421687524389834998653594379505330040944414324922318462661367435552948535038933391534125859187507951480965046083953818647928848658003896737990985228098376121001558383360471722900433242780762637943002535137226721670445921627722109317189409017765653270675696989375853222639804536992076910925848240773917179399164798789544987798423262535504703175519971144794398732092762632950993609006336237864904951234837600385553012058199843288128227237816260673996863861658549207495547290409675004342152418205584651876035289545997152554271104383145350765469462087836467851591017090867020043691652914203336311906685317568835079829222627642387672653678174336733095671773755850528842014455977640646498994079882081994087745908385348896713619659862407447120497149113103175861214059473023519174778240430592661724406298384063532289195314546610851796654330817913522319145236547113735880167626534613939338933885635108863836675341231462496589510355576874080164230588014440040211010409907315679308211290683535050852773381898275886219913321336494736834940804794400051831110019366849631637295682100276596494172419768085818181608416408381289400031736375909617020529061544253387635927871478256491814686850666802256440399836347254940393501831663884036827816729262236715084643562353419841353831948373577880570194191391277755728596145975995216032480455333468277225836792602468933825060939688487672465468037734846973336546530276501342402038577600842110197542784268513723824112897709833477714367217404516890094715861512533359872674916001543666774524399763007361178211940786796649182809426177444220341167264400430401492676181201250890243255375829581431533903168235244591530264836595728041677038512496419171756406280818916165143707717196785787707802032941007343320305101873361684065508107855221600717835429143316554795956667581365727105166653551841396635985446728567504654878964356246379382707400030627943409044916197072314382182808789693880020829888660683043096775611919067723336397702537690916265433778955693399622295458937227341294565418193086260834902589907392013950277178887387092073865028824337021419097600934352434996515913614557062604935484419605691949249664107642815884042801163884371506574026351114048040390253263311423404105802062378431944178585055287512617536477650436427863388294720402557702169098795927887240862567376553854277364564452028701572549766768713122338424453310786183655811104755213800321092094269073953839189242219593418354546768978323763532961652540199575527915095406683552154187032338808692241847329565160992490998437100213578857100540563936194965329168116250720516453600876423736424005909687693424428859332195778224528850882762871457769695544441227039459384048613555690268041400840206047185459979105661356855985871949719655204585651925403727011395548713615341917191140363540817021821817328542103233496406188132718674839769796289385524318687768574173833372772428601798773749149618635844341525451394630457758078984584555397087564601810259093077340296133533875061939870568177008402372121780983127357965157740585883913595992800840762680605079482153542268877905464340583335311964945560934772956597371352082616772190728599788502267735996081082874606459889762253928306320351086367868168537763019686391492650331446851856780664168810015679885420566225937406199660055141832993979345228872322163832925027019419076373792790947760842668852150160997504465602819441874278437387730698391995517856546062085269874048765246522408230448933177377983111766814231297565385706209655205027214744355558186008679066210708284336894922817932325004236183546047232711260622372422123957531937500934836957978036560066210083955395402274817242364622684991220740654772418557927668588997671866831999390853046238114106891352888941141262630853479978628844214700331548415082751242696657818984792645890471236190918675117502834802991543539154194565402421964237067127493481925047765662431928993997617141897444477222600439825168093794107140794791387906120161220105595230766782508512249492212056404331041934003289343582340100622724523491403794247952531457083238297113534083968716333189290747049148925937893817328231185883829790063757671781798434342542440911410754565920601282009847602653277895836793461021712536874642965498878684437868018176598145481639394396805726386248387937520196562277136409487507452505079461219080254656762641989489873766336554873384206780793436809207950684474903839091739114348436085513777882643713165532587749303795000898381071884853558917272148951926704220548730829761010616444420433038873075226847573487819832570756093328025066411483014483074117928995034456447036799698517797530352781872438774982463647794246020997611877148422301487560873285242890513575749701219453763267124843703792726581241625970572617586208342370322223519599339584968870903563338899625736899857868460744036018077467470535523733831822905449778176384174175167492419683036217725591203570456413403894133951896886054587931836259225543945067866266840715450649851563176508242344765070160548594476169167219621714456742993406487416318639510090111369967216174056724290130006082544122112401362012028310387628237522468351120292315985266581039829239929239516363159150781695900588148181106263115808256055636077568535520931121383156182566369175589520378862854777576119289024042419108829620778462682326158012052003991952391388664332127207431866706179300837887445717169810105808656580742468922644656010378224572260275463089242819202717154295305409244844895211416284883850942284251004903667541906972807715350646762179505255878815645561620366045906766250229333601838456133207504077361780943061412799026902167133646513000313898047569706289905392588751468305585272277030223060518026711933549588371409760890472412032062155304729251682295344257611220245203888820632774476554722913337971553959709091140085941191425139186996015144960876606886667832995653862058717605973568346235413137047051138699034933818274488212107116935741821789644170763821781734192058113086712260524216150979612778303507003825002054412570560918992134229037434848140896820360508421082070569815154622042845717799012615364939210929455903215113976625050572014129524598469140799739560247289206874611383450385132884878462591401610904431894203881244617658407574578784401903516072626758625499589103690537932895740977769110302957750332996010812876429286046708885878522367793246049187279478334069545908913226221047873545640820468144724360340534523835349411033859201108321479579312965264473219203105045369190453466621878168678345347611118487568042377638385720746648502803662768570867750638643451584745110780818482608996141173757538861788952622245621078264079617212833208446697092466767221470708143298161961133493150974815321170512118861018087717555267680981191380923668831127068353614854769078061043036732164643748715150681136786845436149122549003714970546568200913265460869714371951370560388789968692438136737146214779945481205380842887228136756747720587175846478424593806068255654525552123670097905566925290939844854447763816174657546710641837839611895787010947676117813919379638758694532330410907586372512779132058411975083525824779560690947379170654811507526565329910723923040649145206873636260755190939574250959022000415928462414252041431995986228757080571335095640647314159937860345167970400765767792060876458946576754456747720071285080557485223693398854500055464853584964145750241515486965486983047574900695615815371086799441301979219041314308523750216501543579506038678473305543411132561499309678719355825645461220692760742875382318950252876628872266495512138129403067075699663213607403291437299807381192843064789802987447914492434299009435312555110757947202904936685920896499909921212383284134796923283665379848668920866209340481529093552564772775441212113371470868405019996211081402276459138597380398455408887860978365958415712137099287722968350978760612686816337076219309550654586753201444515401330185093291466582979118937318078757432149268718788971328665275728820086232278180685588828490079342124616888890439450040115130479436841886478053507566197987953043724563925138337516213805243571021648874657522772326165081896261082795820607202670086265850181911595314125644544974438351395194568081990694594701660587435341657042049081479319179316616263499159937335145604531578140522033075595673161412600520380581025820173224928163880456189932775541224494929426041013434453949520694192698955427155979713537717956338656603132855882573425186476331822800771768783578342727033263033888642903642163342464765252303295255265132834359298183351519515923516038527768304677257424087545230802007284543873474814411964572514036691289651210187051471510897609342517490077732554444059201329477669489173530586704991820730927139128169445311941629443638465954678261842705903657508276566090224672369854221152895201342652308289072890478462044852245183624933498087024552642826045701959943459694553001222678045931863628209352478499848191081342821236813443946935342822874315710812813862723954954114288238394826311334896419417374167093532987799717080810840534785649610926086219307972128147403211523944597362611153239982174078277429985109946946577650832012562998168254231080177588051910595095827067102667700621784151336001616853639587944280228555483915437334997369046800810388112083304352209341527722170985722299218543498715486224491736222333687044867346954666355752912227461594858955346091141722836915088520455145019298511019280520392829966670370837086652517819649059883922012519469719743503605281934059916229350415055983073290909671956168046777259889028065086506568993660688007216249188052555620022986992708254064270606601868604248974790908658216902891236454292806156777576383313244820923540498654226829579259422366770988346085221356066817197914092427351864072735272079810844506420860276135177441880797580714218094265567501676475232018450517200898436182950400420729037648712332497746532754630639255971727118132102956753691667176281105871997856448635867493802325823479729897427881945130669979723832854457824471034689566908359987755791703768223180337876417996506935412333009960239621213878166321109281443725375095763301089885130862355934862620863695034334178446470332997919616940451063884811276870300494819783311198497573614393807019449847907336364656698721139788632856277003972721471831815751294826212580493915496568635694602947986435352815246264359369902928005734636221413452774648101172816383702720575198198347752541724414146845331803844141343187751330281537661502052642175218501933033212127924615983861905081249532784496324072694303757151468706505414325830049650488691483318088480163657502574146802675933947358336938508978207430385038767151091734791396300281750741537167598327304885590640356468703043337068190738965948749201979141331230670077219926464834978217112532016768910719802974191613653580952521807508969666493690863556454023507911220086932957161788553578595228491296304996512959770269630965021039302235476072441969850355010131371027856747901844424395328416932481318705364864538036076125058654975737722164933064588488725572051885991949300625078150561985687098127130693577935718271778158729385735009489698228994651437608754415428765129675121792879000184301065680205154980781953886922047691895820288832955298449617529684201916025164665832766829905510880793279641333524309032765804290526217835174217038758742313188428016040835244950314222107996119331509108914063969187173322100796305256438060758669707101546399538407221187429334737333352067343754432589829573456222083778950216532862549606820415312964250899989329658894534500413950453642053960392918632470511367539405316248777112063514786449028892534320164583520952521536247485254214926883230311557867576386372701475717237694710502499720655187319645100531490146784428633504797925626486517455375356452422498212864562708768163521115119106915962244580326238587351570715273111898929469065348739180640123342334233074051164900711574710469686332262432549554575363087109537551701048827269262469182276946676011062086223116555548241782295870655516706858693509063496973167101114801100466209603189296498948340758142678198213721192645881201089346655351284462090278423594365643723489114496842254992381567596163692606370240558817025922838518452924101787303500621005159326097546216534461404520704476876208474345403151286112509745130637871979949985362125671588554221832402991664691271635684152764684079671616561398711805283228657437940056309383740791508917534161773550267538095030613295622776514627139242465028528937648567015168115592802676387455519275384427200140330675839858129671853469436369604571335159254511305281916224250257041768362852790270999771786908493948535369293575384181545876415803700422385833197037009193605925499885176418176358884852275254765000147908631898263944821134492407405358228763671607972469383340349127743031450132852127388115398218487068433784692005696148205668235244778900093105416478561607194218860389178556047663907328238324270005207740085298745551166634435987761180158404181987451720004773356479846904863374771978425384184579536282566323192262335999014133239179632659487130261394004791570205222214021369869639252392951539259307914959289951571520082048645320358671024599485669109764413373174803352047815133074005692935553600656723147980053211814278011056279331336649524152820729526572336426354977531675759829293923822891708473128308574933715131401163932469518076181928202629580597321411688848163462674469084207220826327472240672040885788231669071843977608867199691581205367781453413023193368027536719029737433342911274663344761991109479206964037533646520806591957094902447067533922132003985778761316698668906984434419744244759871563975510822832580510632227041613282927487739562388583384192402949705600191426657457137362956138893717411354882194540399125955901017501672503142157229161336839064130851113411485359448737163477789233559393704654671091562390418598048891760951594552102766308216207368428585522930347896645748352414256669059445308732109167617100718743331294649833834280854181982704763284732252790619797724001270571805275317392993850961084716250700756221728104653626532543698215747785006591380449909346380809838401995980694562595622672741365760084828074898000642400664149828655429435251649383662306244727549045895628780899083376452749104582097335140229832005368432753202550012597715847575878132716466036410028441262549313292994825704206161463066450044826910013262212334662644499098131876290735735563237760663015280944792573725243169415442319160870135219843451092894761331764742969688238466156913588678835529105220913275283721583005425580000297246645388231680148103974607790484543347472550899010713157275544786253367837983427330645394433063929203220279236566377934130638962360402904898583067269696414248810797447736903056060124676251151218580274761732887528182908128798681520955981783222814758607549538098354748751452039795631855230894350055360023976033012015954447111688513905142270170595138315505772571431321232123585585395378026116157299891546252994624789278993593092747941156261713583419245987323670779639556266816156841831490020323263984607632517761737809992221176957730154832060140563095488510112074431193485062585788163351067363490251025146429270112882794668697360575912582070121323991772724731174529176578710229745388292242491257779752549354430691413446466507201958001464030123333119801056126124458731926125470135434890149285529686092044550329808079330025898458726216061298317532382206001985779091238065265422341207685294308500668226896419603586320962321827953021913504887231829246881290794135022043747160371760756958395297449319562259021958136231117837534517776198659886039780484227659373760915712646537592210120747267255716116444586124490656562020842719462888724045855042947167398490973555728801877268332406466474431314808417457067605141466448743109020043048084282065721115671601731024147119931788959476691479612207299708352529102909327382270910710125253091270810606370660040436483236330729084534448014762831230933001985257030015104677783864289500097934880919308561937115719577320954611519193843598167654136969822921300417291786075764891634538847450506537510386075361694310403780506205282662101365352186743146602633838432912922570525291519288579641248090727925617530303751688573397399714431715246196089180694107513521921504799998505700318465948497082832015838055255825413189745894532944998985683870096526876120310119262253912887486351941617156406513015731491959742442850640369024676778593868945700299308623803666101751387957903218341225688921334955784088953701376044401156927989007668551285485712982813423045228679616021266216135121152366192593894375499942021390305725185996356545222804748350748661218036246761263432461316575230431117947814973793575048170013641486686853841571800304442900266332437296028202146777079414821890116842315799998474709113625665323209802289151965597317313201357923713512726308988572132782612961940130043743651023896196574584956819972662531174446414723004365395789210340607112688510093225367623202909054111789229641409873687942777383551608152809511555414049457509978296043042392571564961526391636928263603686194819841984308389647194584202709361338225467189403633616661600938043739697495190323537108921105798933101754010676651510293888045793538676819281662235291134039355209770879776083561762251763359305255534065966458825374977636626965593938535973143726385286031459408385306279055193210899007222228848337646083857020262704426602399804109055436630545836234463859009503281931858753339048955921615641167688613611768531243333458827903206699590374634301208777801273171505265983341528567330053758208217088239585183823529132069451763477439200584202731169930423454944457519913965750786074334490475105806669265140617808524058945344515852791909910695856616976398218486293047468464240000107646746871947389972844082644371970179117342846064907459662263433526586601024140781941980161949288024725515569978667243696395240320149702436154659996152522828412000937343109729806551229813202940946728619586862761920197348305135596176885727998829870799183952332215941296856497313651664952094174731665352439443898156281488617264177137867366004496510576340371997593393630617753405575362566454577617223570767152783724296490864042776721760375530485100137942027262614262078015167907504279183134455423688288360880700416394698102884590074657384843035605790478741200750969317734709366114474765625841114847968639859844792781923419609699431431499452604482101015861469776645688124724771286910906663640607009315536637364790983643355203983637670015862115092375305567745113563632883863098023990476779499922080256799608278667692073458071542149750237716406568229299708202002415812031221902002616122230175716672868876177790906202154591394666958221075888250857755353683996635638687660616247275176615243965428812810891805565695819508409507955925738538563530945115753143707846352464424723776305647764358062824628740619994365872019736176896244120554332483228205433710714881060968483021191323757419132660364760491892968666963852855723318360743915062463936570928624090331126496989658665920742493411739988194208596693642189745090596947721290255329725531622281344268796373146648237095122864204311099972379608420710904463277627788422482686076150479074040481711248440221586590049140075997451985302547430759542107845426647863739691219578032044493334326461735350788972776689920205445582717842107888862227274510721546292096764241290033071028629693503192596449329162270109957414066677450090625616062607210428393495109756309073223039023541186849167031492789329883546805587056695476438692175162298884183902280818826337785529937692259753234142070143734842379657406293269586347042690460527976279001503893156270591075100696215084075887188912886182491318527766326270462190980217912698247134535869820125482074694110361181813146621761073739834243937433360578651008954757580951037870007960960861803655585926848699515047010181105025026642302380290860454114991444682751677842131256758260180224153232759281144413675868250515431387822227601464047427460697469984335903442792960183695054111087348032935980668859102842246595966642113964347569956789424484744365628521404068369412712550221399858664205641068743969472944895078682618967670534432664336225485431394695160192115215899144729642335467194468818900471340578358758590009267974103350579518554071758143017756542758029090508980414524410318956903020734566414945692921735594013519650316439197192957508182371517543516822586725866672338138855823380987688598018053209781918484604353852467218346689524935735444258103282933748400618643546401867056038740844576459695807207475199089457000788510308564635823703406920755496338187874596270119442338679706317787994792324482090786976366390393011374942770024198090926620409741063232355868711377675820644748727226182270668315857050354064718282173570253010254911816056283899596846643691631882227224620344719252793077193056982753057324475649038030242904791363007455134666225049553727982563995367885882166299454210539376635528670878309083748043387393721708324149939288169837398729847083090672445367795425149759991526112013291853658614842436611627614374322864984761106796119447441934672940275979236515220075981619788354810566536319044344864626758227052810105189333731898814838738096381561622271631946943396673310345030473363723333392869598430278850438525175029095437514128408367654510244589804959585022429839752650257929976147667905279706906290204744340742028501568429602868867580373185050481749934524839478106697795916355153877177510728898895944059916223733954815595266257190467999765112117275043424006899854838694897277006699325021283664083622777061838209993022057426661311386711079849744108978232753881765424290255188321530558380195474138376625214261499601891566655137778453115266774569589735246382155721565058380401194776860493312272877617862466356415193273293400929618807753277641745221217445066692503273868437087019905650232461345384517679318653280845875

Execution Trace

WooCrossChainRouterV2.60a06040( )
// SPDX-License-Identifier: MIT
pragma solidity =0.8.14;
// OpenZeppelin Contracts
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {ICommonOFT, IOFTWithFee} from "@layerzerolabs/solidity-examples/contracts/token/oft/v2/fee/IOFTWithFee.sol";
// Local Contracts
import {IWETH} from "./interfaces/IWETH.sol";
import {IWooCrossChainRouterV2} from "./interfaces/IWooCrossChainRouterV2.sol";
import {IWooRouterV2} from "./interfaces/IWooRouterV2.sol";
import {IStargateEthVault} from "./interfaces/Stargate/IStargateEthVault.sol";
import {IStargateRouter} from "./interfaces/Stargate/IStargateRouter.sol";
import {ILzApp} from "./interfaces/LayerZero/ILzApp.sol";
import {TransferHelper} from "./libraries/TransferHelper.sol";
/// @title WOOFi cross chain router implementation.
/// @notice Router for stateless execution of cross chain swap against WOOFi private pool.
/// @custom:stargate-contracts https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet
contract WooCrossChainRouterV2 is IWooCrossChainRouterV2, Ownable, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;
    /* ----- Constants ----- */
    address public constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    /* ----- Variables ----- */
    IWooRouterV2 public wooRouter;
    IStargateRouter public stargateRouter;
    address public immutable weth;
    uint256 public bridgeSlippage; // 1 in 10000th: default 1%
    uint256 public dstGasForSwapCall;
    uint256 public dstGasForNoSwapCall;
    uint16 public sgChainIdLocal; // Stargate chainId on local chain
    mapping(uint16 => address) public wooCrossChainRouters; // chainId => WooCrossChainRouter address
    mapping(uint16 => address) public sgETHs; // chainId => SGETH token address
    mapping(uint16 => mapping(address => uint256)) public sgPoolIds; // chainId => token address => Stargate poolId
    mapping(address => address) public tokenToOFTs; // token address(sgChainIdLocal) => OFT address
    EnumerableSet.AddressSet private directBridgeTokens;
    receive() external payable {}
    constructor(
        address _weth,
        address _wooRouter,
        address _stargateRouter,
        uint16 _sgChainIdLocal
    ) {
        wooRouter = IWooRouterV2(_wooRouter);
        stargateRouter = IStargateRouter(_stargateRouter);
        weth = _weth;
        bridgeSlippage = 100;
        dstGasForSwapCall = 360000;
        dstGasForNoSwapCall = 80000;
        sgChainIdLocal = _sgChainIdLocal;
        _initSgETHs();
        _initSgPoolIds();
        _initTokenToOFTs(_sgChainIdLocal);
    }
    /* ----- Functions ----- */
    function crossSwap(
        uint256 refId,
        address payable to,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) external payable nonReentrant {
        require(srcInfos.fromToken != address(0), "WooCrossChainRouterV2: !srcInfos.fromToken");
        require(
            dstInfos.toToken != address(0) && dstInfos.toToken != sgETHs[dstInfos.chainId],
            "WooCrossChainRouterV2: !dstInfos.toToken"
        );
        require(to != address(0), "WooCrossChainRouterV2: !to");
        uint256 msgValue = msg.value;
        uint256 bridgeAmount;
        {
            // Step 1: transfer
            if (srcInfos.fromToken == ETH_PLACEHOLDER_ADDR) {
                require(srcInfos.fromAmount <= msgValue, "WooCrossChainRouterV2: !srcInfos.fromAmount");
                srcInfos.fromToken = weth;
                IWETH(weth).deposit{value: srcInfos.fromAmount}();
                msgValue -= srcInfos.fromAmount;
            } else {
                TransferHelper.safeTransferFrom(srcInfos.fromToken, msg.sender, address(this), srcInfos.fromAmount);
            }
            // Step 2: local swap by WooRouter or not
            // 1.WOO is directBridgeToken, path(always) WOO(Arbitrum) => WOO(BSC)
            // 2.WOO not the directBridgeToken, path(maybe): WOO(Arbitrum) -> USDC(Arbitrum) => BUSD(BSC) -> WOO(BSC)
            // 3.Ethereum no WOOFi liquidity, tokens(WOO, ETH, USDC) always will be bridged directly without swap
            if (!directBridgeTokens.contains(srcInfos.fromToken) && srcInfos.fromToken != srcInfos.bridgeToken) {
                TransferHelper.safeApprove(srcInfos.fromToken, address(wooRouter), srcInfos.fromAmount);
                bridgeAmount = wooRouter.swap(
                    srcInfos.fromToken,
                    srcInfos.bridgeToken,
                    srcInfos.fromAmount,
                    srcInfos.minBridgeAmount,
                    payable(address(this)),
                    to
                );
            } else {
                require(
                    srcInfos.fromAmount == srcInfos.minBridgeAmount,
                    "WooCrossChainRouterV2: !srcInfos.minBridgeAmount"
                );
                bridgeAmount = srcInfos.fromAmount;
            }
            require(
                bridgeAmount <= IERC20(srcInfos.bridgeToken).balanceOf(address(this)),
                "WooCrossChainRouterV2: !bridgeAmount"
            );
        }
        // Step 3: cross chain swap by [OFT / StargateRouter]
        address oft = tokenToOFTs[srcInfos.bridgeToken];
        if (oft != address(0)) {
            _bridgeByOFT(refId, to, msgValue, bridgeAmount, IOFTWithFee(oft), srcInfos, dstInfos);
        } else {
            _bridgeByStargate(refId, to, msgValue, bridgeAmount, srcInfos, dstInfos);
        }
        emit WooCrossSwapOnSrcChain(
            refId,
            _msgSender(),
            to,
            srcInfos.fromToken,
            srcInfos.fromAmount,
            srcInfos.minBridgeAmount,
            bridgeAmount
        );
    }
    function onOFTReceived(
        uint16 srcChainId,
        bytes memory, // srcAddress
        uint64, // nonce
        bytes32 from,
        uint256 amountLD,
        bytes memory payload
    ) external {
        require(_isLegitOFT(_msgSender()), "WooCrossChainRouterV2: INVALID_CALLER");
        require(
            wooCrossChainRouters[srcChainId] == address(uint160(uint256(from))),
            "WooCrossChainRouterV2: INVALID_FROM"
        );
        // _msgSender() should be OFT address if requires above are passed
        address bridgedToken = IOFTWithFee(_msgSender()).token();
        // make sure the same order to abi.encode when decode payload
        (uint256 refId, address to, address toToken, uint256 minToAmount) = abi.decode(
            payload,
            (uint256, address, address, uint256)
        );
        _handleERC20Received(refId, to, toToken, bridgedToken, amountLD, minToAmount);
    }
    function sgReceive(
        uint16, // srcChainId
        bytes memory, // srcAddress
        uint256, // nonce
        address bridgedToken,
        uint256 amountLD,
        bytes memory payload
    ) external {
        require(msg.sender == address(stargateRouter), "WooCrossChainRouterV2: INVALID_CALLER");
        // make sure the same order to abi.encode when decode payload
        (uint256 refId, address to, address toToken, uint256 minToAmount) = abi.decode(
            payload,
            (uint256, address, address, uint256)
        );
        // toToken won't be SGETH, and bridgedToken won't be ETH_PLACEHOLDER_ADDR
        if (bridgedToken == sgETHs[sgChainIdLocal]) {
            // bridgedToken is SGETH, received native token
            _handleNativeReceived(refId, to, toToken, amountLD, minToAmount);
        } else {
            // bridgedToken is not SGETH, received ERC20 token
            _handleERC20Received(refId, to, toToken, bridgedToken, amountLD, minToAmount);
        }
    }
    function quoteLayerZeroFee(
        uint256 refId,
        address to,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) external view returns (uint256, uint256) {
        bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount);
        address oft = tokenToOFTs[srcInfos.bridgeToken];
        if (oft != address(0)) {
            // bridge via OFT if it's OFT
            uint256 dstGasForCall = _getDstGasForCall(dstInfos);
            bytes memory adapterParams = _getAdapterParams(to, oft, dstGasForCall, dstInfos);
            bool useZro = false;
            bytes32 dstWooCrossChainRouter = bytes32(uint256(uint160(wooCrossChainRouters[dstInfos.chainId])));
            return
                IOFTWithFee(oft).estimateSendAndCallFee(
                    dstInfos.chainId,
                    dstWooCrossChainRouter,
                    srcInfos.minBridgeAmount,
                    payload,
                    uint64(dstGasForCall),
                    useZro,
                    adapterParams
                );
        } else {
            // otherwise bridge via Stargate
            IStargateRouter.lzTxObj memory obj = _getLzTxObj(to, dstInfos);
            return
                stargateRouter.quoteLayerZeroFee(
                    dstInfos.chainId,
                    1, // https://stargateprotocol.gitbook.io/stargate/developers/function-types
                    obj.dstNativeAddr,
                    payload,
                    obj
                );
        }
    }
    function allDirectBridgeTokens() external view returns (address[] memory) {
        uint256 length = directBridgeTokens.length();
        address[] memory tokens = new address[](length);
        unchecked {
            for (uint256 i = 0; i < length; ++i) {
                tokens[i] = directBridgeTokens.at(i);
            }
        }
        return tokens;
    }
    function allDirectBridgeTokensLength() external view returns (uint256) {
        return directBridgeTokens.length();
    }
    function _initSgETHs() internal {
        // Ethereum
        sgETHs[101] = 0x72E2F4830b9E45d52F80aC08CB2bEC0FeF72eD9c;
        // Arbitrum
        sgETHs[110] = 0x82CbeCF39bEe528B5476FE6d1550af59a9dB6Fc0;
        // Optimism
        sgETHs[111] = 0xb69c8CBCD90A39D8D3d3ccf0a3E968511C3856A0;
    }
    function _initSgPoolIds() internal {
        // poolId > 0 means able to be bridge token
        // Ethereum
        sgPoolIds[101][0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48] = 1; // USDC
        sgPoolIds[101][0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2] = 13; // WETH
        sgPoolIds[101][0x4691937a7508860F876c9c0a2a617E7d9E945D4B] = 20; // WOO
        // BNB Chain
        sgPoolIds[102][0x55d398326f99059fF775485246999027B3197955] = 2; // USDT
        sgPoolIds[102][0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56] = 5; // BUSD
        sgPoolIds[102][0x4691937a7508860F876c9c0a2a617E7d9E945D4B] = 20; // WOO
        // Avalanche
        sgPoolIds[106][0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E] = 1; // USDC
        sgPoolIds[106][0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7] = 2; // USDT
        sgPoolIds[106][0xaBC9547B534519fF73921b1FBA6E672b5f58D083] = 20; // WOO
        // Polygon
        sgPoolIds[109][0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174] = 1; // USDC
        sgPoolIds[109][0xc2132D05D31c914a87C6611C10748AEb04B58e8F] = 2; // USDT
        sgPoolIds[109][0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603] = 20; // WOO
        // Arbitrum
        sgPoolIds[110][0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8] = 1; // USDC
        sgPoolIds[110][0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9] = 2; // USDT
        sgPoolIds[110][0x82aF49447D8a07e3bd95BD0d56f35241523fBab1] = 13; // WETH
        sgPoolIds[110][0xcAFcD85D8ca7Ad1e1C6F82F651fA15E33AEfD07b] = 20; // WOO
        // Optimism
        sgPoolIds[111][0x7F5c764cBc14f9669B88837ca1490cCa17c31607] = 1; // USDC
        sgPoolIds[111][0x4200000000000000000000000000000000000006] = 13; // WETH
        sgPoolIds[111][0x871f2F2ff935FD1eD867842FF2a7bfD051A5E527] = 20; // WOO
        // Fantom
        sgPoolIds[112][0x04068DA6C83AFCFA0e13ba15A6696662335D5B75] = 1; // USDC
        sgPoolIds[112][0x6626c47c00F1D87902fc13EECfaC3ed06D5E8D8a] = 20; // WOO
    }
    function _initTokenToOFTs(uint16 _sgChainIdLocal) internal {
        address btcbOFT = 0x2297aEbD383787A160DD0d9F71508148769342E3; // BTCbOFT && BTCbProxyOFT
        if (_sgChainIdLocal == 106) {
            // BTC.b(ERC20) on Avalanche address
            tokenToOFTs[0x152b9d0FdC40C096757F570A51E494bd4b943E50] = btcbOFT;
        }
        tokenToOFTs[btcbOFT] = btcbOFT;
    }
    function _getDstGasForCall(DstInfos memory dstInfos) internal view returns (uint256) {
        return (dstInfos.toToken == dstInfos.bridgeToken) ? dstGasForNoSwapCall : dstGasForSwapCall;
    }
    function _getAdapterParams(
        address to,
        address oft,
        uint256 dstGasForCall,
        DstInfos memory dstInfos
    ) internal view returns (bytes memory) {
        // OFT src logic: require(providedGasLimit >= minGasLimit)
        // uint256 minGasLimit = minDstGasLookup[_dstChainId][_type] + dstGasForCall;
        // _type: 0(send), 1(send_and_call)
        uint256 providedGasLimit = ILzApp(oft).minDstGasLookup(dstInfos.chainId, 1) + dstGasForCall;
        // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters#airdrop
        return
            abi.encodePacked(
                uint16(2), // version: 2 is able to airdrop native token on destination but 1 is not
                providedGasLimit, // gasAmount: destination transaction gas for LayerZero to delivers
                dstInfos.airdropNativeAmount, // nativeForDst: airdrop native token amount
                to // addressOnDst: address to receive airdrop native token on destination
            );
    }
    function _getLzTxObj(address to, DstInfos memory dstInfos) internal view returns (IStargateRouter.lzTxObj memory) {
        uint256 dstGasForCall = _getDstGasForCall(dstInfos);
        return IStargateRouter.lzTxObj(dstGasForCall, dstInfos.airdropNativeAmount, abi.encodePacked(to));
    }
    function _isLegitOFT(address caller) internal view returns (bool) {
        return tokenToOFTs[caller] != address(0);
    }
    function _bridgeByOFT(
        uint256 refId,
        address payable to,
        uint256 msgValue,
        uint256 bridgeAmount,
        IOFTWithFee oft,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) internal {
        {
            address token = oft.token();
            require(token == srcInfos.bridgeToken, "WooCrossChainRouterV2: !token");
            if (token != address(oft)) {
                // oft.token() != address(oft) means is a ProxyOFT
                // for example: BTC.b on Avalanche is ERC20, need BTCbProxyOFT to lock up BTC.b
                TransferHelper.safeApprove(srcInfos.bridgeToken, address(oft), bridgeAmount);
            }
        }
        // OFT src logic: require(_removeDust(bridgeAmount) >= minAmount)
        uint256 minAmount = (bridgeAmount * (10000 - bridgeSlippage)) / 10000;
        bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount);
        uint256 dstGasForCall = _getDstGasForCall(dstInfos);
        ICommonOFT.LzCallParams memory callParams;
        {
            bytes memory adapterParams = _getAdapterParams(to, address(oft), dstGasForCall, dstInfos);
            callParams = ICommonOFT.LzCallParams(
                payable(msg.sender), // refundAddress
                address(0), // zroPaymentAddress
                adapterParams //adapterParams
            );
        }
        bytes32 dstWooCrossChainRouter = bytes32(uint256(uint160(wooCrossChainRouters[dstInfos.chainId])));
        oft.sendAndCall{value: msgValue}(
            address(this),
            dstInfos.chainId,
            dstWooCrossChainRouter,
            bridgeAmount,
            minAmount,
            payload,
            uint64(dstGasForCall),
            callParams
        );
    }
    function _bridgeByStargate(
        uint256 refId,
        address payable to,
        uint256 msgValue,
        uint256 bridgeAmount,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) internal {
        uint256 srcPoolId = sgPoolIds[sgChainIdLocal][srcInfos.bridgeToken];
        require(srcPoolId > 0, "WooCrossChainRouterV2: !srcInfos.bridgeToken");
        uint256 dstPoolId = sgPoolIds[dstInfos.chainId][dstInfos.bridgeToken];
        require(dstPoolId > 0, "WooCrossChainRouterV2: !dstInfos.bridgeToken");
        bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount);
        uint256 dstMinBridgeAmount = (bridgeAmount * (10000 - bridgeSlippage)) / 10000;
        bytes memory dstWooCrossChainRouter = abi.encodePacked(wooCrossChainRouters[dstInfos.chainId]);
        IStargateRouter.lzTxObj memory obj = _getLzTxObj(to, dstInfos);
        if (srcInfos.bridgeToken == weth) {
            IWETH(weth).withdraw(bridgeAmount);
            address sgETH = sgETHs[sgChainIdLocal];
            IStargateEthVault(sgETH).deposit{value: bridgeAmount}(); // logic from Stargate RouterETH.sol
            TransferHelper.safeApprove(sgETH, address(stargateRouter), bridgeAmount);
        } else {
            TransferHelper.safeApprove(srcInfos.bridgeToken, address(stargateRouter), bridgeAmount);
        }
        stargateRouter.swap{value: msgValue}(
            dstInfos.chainId, // dst chain id
            srcPoolId, // bridge token's pool id on src chain
            dstPoolId, // bridge token's pool id on dst chain
            payable(_msgSender()), // rebate address
            bridgeAmount, // swap amount on src chain
            dstMinBridgeAmount, // min received amount on dst chain
            obj, // config: dstGasForCall, dstAirdropNativeAmount, dstReceiveAirdropNativeTokenAddr
            dstWooCrossChainRouter, // smart contract to call on dst chain
            payload // payload to piggyback
        );
    }
    function _handleNativeReceived(
        uint256 refId,
        address to,
        address toToken,
        uint256 bridgedAmount,
        uint256 minToAmount
    ) internal {
        address msgSender = _msgSender();
        if (toToken == ETH_PLACEHOLDER_ADDR) {
            TransferHelper.safeTransferETH(to, bridgedAmount);
            emit WooCrossSwapOnDstChain(
                refId,
                msgSender,
                to,
                weth,
                bridgedAmount,
                toToken,
                ETH_PLACEHOLDER_ADDR,
                minToAmount,
                bridgedAmount
            );
        } else {
            try
                wooRouter.swap{value: bridgedAmount}(
                    ETH_PLACEHOLDER_ADDR,
                    toToken,
                    bridgedAmount,
                    minToAmount,
                    payable(to),
                    to
                )
            returns (uint256 realToAmount) {
                emit WooCrossSwapOnDstChain(
                    refId,
                    msgSender,
                    to,
                    weth,
                    bridgedAmount,
                    toToken,
                    toToken,
                    minToAmount,
                    realToAmount
                );
            } catch {
                TransferHelper.safeTransferETH(to, bridgedAmount);
                emit WooCrossSwapOnDstChain(
                    refId,
                    msgSender,
                    to,
                    weth,
                    bridgedAmount,
                    toToken,
                    ETH_PLACEHOLDER_ADDR,
                    minToAmount,
                    bridgedAmount
                );
            }
        }
    }
    function _handleERC20Received(
        uint256 refId,
        address to,
        address toToken,
        address bridgedToken,
        uint256 bridgedAmount,
        uint256 minToAmount
    ) internal {
        address msgSender = _msgSender();
        if (toToken == bridgedToken) {
            TransferHelper.safeTransfer(bridgedToken, to, bridgedAmount);
            emit WooCrossSwapOnDstChain(
                refId,
                msgSender,
                to,
                bridgedToken,
                bridgedAmount,
                toToken,
                toToken,
                minToAmount,
                bridgedAmount
            );
        } else {
            TransferHelper.safeApprove(bridgedToken, address(wooRouter), bridgedAmount);
            try wooRouter.swap(bridgedToken, toToken, bridgedAmount, minToAmount, payable(to), to) returns (
                uint256 realToAmount
            ) {
                emit WooCrossSwapOnDstChain(
                    refId,
                    msgSender,
                    to,
                    bridgedToken,
                    bridgedAmount,
                    toToken,
                    toToken,
                    minToAmount,
                    realToAmount
                );
            } catch {
                TransferHelper.safeTransfer(bridgedToken, to, bridgedAmount);
                emit WooCrossSwapOnDstChain(
                    refId,
                    msgSender,
                    to,
                    bridgedToken,
                    bridgedAmount,
                    toToken,
                    bridgedToken,
                    minToAmount,
                    bridgedAmount
                );
            }
        }
    }
    /* ----- Owner & Admin Functions ----- */
    function setWooRouter(address _wooRouter) external onlyOwner {
        require(_wooRouter != address(0), "WooCrossChainRouterV2: !_wooRouter");
        wooRouter = IWooRouterV2(_wooRouter);
    }
    function setStargateRouter(address _stargateRouter) external onlyOwner {
        require(_stargateRouter != address(0), "WooCrossChainRouterV2: !_stargateRouter");
        stargateRouter = IStargateRouter(_stargateRouter);
    }
    function setBridgeSlippage(uint256 _bridgeSlippage) external onlyOwner {
        require(_bridgeSlippage <= 10000, "WooCrossChainRouterV2: !_bridgeSlippage");
        bridgeSlippage = _bridgeSlippage;
    }
    function setDstGasForSwapCall(uint256 _dstGasForSwapCall) external onlyOwner {
        dstGasForSwapCall = _dstGasForSwapCall;
    }
    function setDstGasForNoSwapCall(uint256 _dstGasForNoSwapCall) external onlyOwner {
        dstGasForNoSwapCall = _dstGasForNoSwapCall;
    }
    function setSgChainIdLocal(uint16 _sgChainIdLocal) external onlyOwner {
        sgChainIdLocal = _sgChainIdLocal;
    }
    function setWooCrossChainRouter(uint16 chainId, address wooCrossChainRouter) external onlyOwner {
        require(wooCrossChainRouter != address(0), "WooCrossChainRouterV2: !wooCrossChainRouter");
        wooCrossChainRouters[chainId] = wooCrossChainRouter;
    }
    function setSgETH(uint16 chainId, address token) external onlyOwner {
        require(token != address(0), "WooCrossChainRouterV2: !token");
        sgETHs[chainId] = token;
    }
    function setSgPoolId(
        uint16 chainId,
        address token,
        uint256 poolId
    ) external onlyOwner {
        sgPoolIds[chainId][token] = poolId;
    }
    function setTokenToOFT(address token, address oft) external onlyOwner {
        tokenToOFTs[token] = oft;
    }
    function addDirectBridgeToken(address token) external onlyOwner {
        bool success = directBridgeTokens.add(token);
        require(success, "WooCrossChainRouterV2: token exist");
    }
    function removeDirectBridgeToken(address token) external onlyOwner {
        bool success = directBridgeTokens.remove(token);
        require(success, "WooCrossChainRouterV2: token not exist");
    }
    function inCaseTokenGotStuck(address stuckToken) external onlyOwner {
        if (stuckToken == ETH_PLACEHOLDER_ADDR) {
            TransferHelper.safeTransferETH(msg.sender, address(this).balance);
        } else {
            uint256 amount = IERC20(stuckToken).balanceOf(address(this));
            TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
        }
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Wrapped ETH.
interface IWETH {
    /// @dev Deposit ETH into WETH
    function deposit() external payable;
    /// @dev Transfer WETH to receiver
    /// @param to address of WETH receiver
    /// @param value amount of WETH to transfer
    /// @return get true when succeed, else false
    function transfer(address to, uint256 value) external returns (bool);
    /// @dev Withdraw WETH to ETH
    function withdraw(uint256) external;
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.14;
/*
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/// @title WOOFi cross chain router interface (version 2).
/// @notice functions to interface with WOOFi cross chain swap.
interface IWooCrossChainRouterV2 {
    /* ----- Structs ----- */
    struct SrcInfos {
        address fromToken;
        address bridgeToken;
        uint256 fromAmount;
        uint256 minBridgeAmount;
    }
    struct DstInfos {
        uint16 chainId;
        address toToken;
        address bridgeToken;
        uint256 minToAmount;
        uint256 airdropNativeAmount;
    }
    /* ----- Events ----- */
    event WooCrossSwapOnSrcChain(
        uint256 indexed refId,
        address indexed sender,
        address indexed to,
        address fromToken,
        uint256 fromAmount,
        uint256 minBridgeAmount,
        uint256 realBridgeAmount
    );
    event WooCrossSwapOnDstChain(
        uint256 indexed refId,
        address indexed sender,
        address indexed to,
        address bridgedToken,
        uint256 bridgedAmount,
        address toToken,
        address realToToken,
        uint256 minToAmount,
        uint256 realToAmount
    );
    /* ----- State Variables ----- */
    function weth() external view returns (address);
    function bridgeSlippage() external view returns (uint256);
    function dstGasForSwapCall() external view returns (uint256);
    function dstGasForNoSwapCall() external view returns (uint256);
    function sgChainIdLocal() external view returns (uint16);
    function wooCrossChainRouters(uint16 chainId) external view returns (address wooCrossChainRouter);
    function sgETHs(uint16 chainId) external view returns (address sgETH);
    function sgPoolIds(uint16 chainId, address token) external view returns (uint256 poolId);
    /* ----- Functions ----- */
    function crossSwap(
        uint256 refId,
        address payable to,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) external payable;
    function sgReceive(
        uint16 srcChainId,
        bytes memory srcAddress,
        uint256 nonce,
        address bridgedToken,
        uint256 amountLD,
        bytes memory payload
    ) external;
    function quoteLayerZeroFee(
        uint256 refId,
        address to,
        SrcInfos memory srcInfos,
        DstInfos memory dstInfos
    ) external view returns (uint256 nativeAmount, uint256 zroAmount);
    function allDirectBridgeTokens() external view returns (address[] memory tokens);
    function allDirectBridgeTokensLength() external view returns (uint256 length);
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.14;
/*
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import "../interfaces/IWooPPV2.sol";
/// @title Woo router interface (version 2)
/// @notice functions to interface with WooFi swap
interface IWooRouterV2 {
    /* ----- Type declarations ----- */
    enum SwapType {
        WooSwap,
        DodoSwap
    }
    /* ----- Events ----- */
    event WooRouterSwap(
        SwapType swapType,
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address from,
        address indexed to,
        address rebateTo
    );
    event WooPoolChanged(address newPool);
    /* ----- Router properties ----- */
    function WETH() external view returns (address);
    function wooPool() external view returns (IWooPPV2);
    /* ----- Main query & swap APIs ----- */
    /// @notice query the amount to swap fromToken -> toToken
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @return toAmount the predicted amount to receive
    function querySwap(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view returns (uint256 toAmount);
    /// @notice query the amount to swap fromToken -> toToken,
    ///     WITHOUT checking the reserve balance; so it
    ///     always returns the quoted amount (for reference).
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @return toAmount the predicted amount to receive
    function tryQuerySwap(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view returns (uint256 toAmount);
    /// @notice Swap `fromToken` to `toToken`.
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of `fromToken` to swap
    /// @param minToAmount the minimum amount of `toToken` to receive
    /// @param to the destination address
    /// @param rebateTo the rebate address (optional, can be 0)
    /// @return realToAmount the amount of toToken to receive
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        address rebateTo
    ) external payable returns (uint256 realToAmount);
    /* ----- 3rd party DEX swap ----- */
    /// @notice swap fromToken -> toToken via an external 3rd swap
    /// @param approveTarget the contract address for token transfer approval
    /// @param swapTarget the contract address for swap
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @param minToAmount the min amount of swapped toToken
    /// @param to the destination address
    /// @param data call data for external call
    function externalSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        bytes calldata data
    ) external payable returns (uint256 realToAmount);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper::safeApprove: approve failed"
        );
    }
    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper::safeTransfer: transfer failed"
        );
    }
    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper::transferFrom: transferFrom failed"
        );
    }
    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, "TransferHelper::safeTransferETH: ETH transfer failed");
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStargateEthVault {
    function deposit() external payable;
    function transfer(address to, uint256 value) external returns (bool);
    function withdraw(uint256) external;
    function approve(address guy, uint256 wad) external returns (bool);
    function transferFrom(
        address src,
        address dst,
        uint256 wad
    ) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStargateRouter {
    struct lzTxObj {
        uint256 dstGasForCall;
        uint256 dstNativeAmount;
        bytes dstNativeAddr;
    }
    function addLiquidity(
        uint256 _poolId,
        uint256 _amountLD,
        address _to
    ) external;
    function swap(
        uint16 _dstChainId,
        uint256 _srcPoolId,
        uint256 _dstPoolId,
        address payable _refundAddress,
        uint256 _amountLD,
        uint256 _minAmountLD,
        lzTxObj memory _lzTxParams,
        bytes calldata _to,
        bytes calldata _payload
    ) external payable;
    function redeemRemote(
        uint16 _dstChainId,
        uint256 _srcPoolId,
        uint256 _dstPoolId,
        address payable _refundAddress,
        uint256 _amountLP,
        uint256 _minAmountLD,
        bytes calldata _to,
        lzTxObj memory _lzTxParams
    ) external payable;
    function instantRedeemLocal(
        uint16 _srcPoolId,
        uint256 _amountLP,
        address _to
    ) external returns (uint256);
    function redeemLocal(
        uint16 _dstChainId,
        uint256 _srcPoolId,
        uint256 _dstPoolId,
        address payable _refundAddress,
        uint256 _amountLP,
        bytes calldata _to,
        lzTxObj memory _lzTxParams
    ) external payable;
    function sendCredits(
        uint16 _dstChainId,
        uint256 _srcPoolId,
        uint256 _dstPoolId,
        address payable _refundAddress
    ) external payable;
    function quoteLayerZeroFee(
        uint16 _dstChainId,
        uint8 _functionType,
        bytes calldata _toAddress,
        bytes calldata _transferAndCallPayload,
        lzTxObj memory _lzTxParams
    ) external view returns (uint256, uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
 * @dev Interface of the LzApp that functions not exist in the @layerzerolabs package
 */
interface ILzApp {
    function minDstGasLookup(uint16 _dstChainId, uint16 _type) external view returns (uint256 _minGasLimit);
}
// 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 v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.
    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status;
    constructor() {
        _status = _NOT_ENTERED;
    }
    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
        _;
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (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.7.0) (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.
    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];
        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.
            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;
            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];
                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }
            // Delete the slot where the moved value was stored
            set._values.pop();
            // Delete the index for the deleted slot
            delete set._indexes[value];
            return true;
        } else {
            return false;
        }
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }
    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }
    // Bytes32Set
    struct Bytes32Set {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }
    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }
    // AddressSet
    struct AddressSet {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }
    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;
        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }
        return result;
    }
    // UintSet
    struct UintSet {
        Set _inner;
    }
    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }
    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }
    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }
    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }
    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;
        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }
        return result;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import "../ICommonOFT.sol";
/**
 * @dev Interface of the IOFT core standard
 */
interface IOFTWithFee is ICommonOFT {
    /**
     * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from`
     * `_from` the owner of token
     * `_dstChainId` the destination chain identifier
     * `_toAddress` can be any size depending on the `dstChainId`.
     * `_amount` the quantity of tokens in wei
     * `_minAmount` the minimum amount of tokens to receive on dstChain
     * `_refundAddress` the address LayerZero refunds if too much message fee is sent
     * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token)
     * `_adapterParams` is a flexible bytes array to indicate messaging adapter services
     */
    function sendFrom(address _from, uint16 _dstChainId, bytes32 _toAddress, uint _amount, uint _minAmount, LzCallParams calldata _callParams) external payable;
    function sendAndCall(address _from, uint16 _dstChainId, bytes32 _toAddress, uint _amount, uint _minAmount, bytes calldata _payload, uint64 _dstGasForCall, LzCallParams calldata _callParams) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.14;
/*
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/// @title Woo private pool for swap.
/// @notice Use this contract to directly interfact with woo's synthetic proactive
///         marketing making pool.
/// @author woo.network
interface IWooPPV2 {
    /* ----- Events ----- */
    event Deposit(address indexed token, address indexed sender, uint256 amount);
    event Withdraw(address indexed token, address indexed receiver, uint256 amount);
    event Migrate(address indexed token, address indexed receiver, uint256 amount);
    event AdminUpdated(address indexed addr, bool flag);
    event FeeAddrUpdated(address indexed newFeeAddr);
    event WooracleUpdated(address indexed newWooracle);
    event WooSwap(
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address from,
        address indexed to,
        address rebateTo,
        uint256 swapVol,
        uint256 swapFee
    );
    /* ----- External Functions ----- */
    /// @notice The quote token address (immutable).
    /// @return address of quote token
    function quoteToken() external view returns (address);
    /// @notice Gets the pool size of the specified token (swap liquidity).
    /// @param token the token address
    /// @return the pool size
    function poolSize(address token) external view returns (uint256);
    /// @notice Query the amount to swap `fromToken` to `toToken`, without checking the pool reserve balance.
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of `fromToken` to swap
    /// @return toAmount the swapped amount of `toToken`
    function tryQuery(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view returns (uint256 toAmount);
    /// @notice Query the amount to swap `fromToken` to `toToken`, with checking the pool reserve balance.
    /// @dev tx reverts when 'toToken' balance is insufficient.
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of `fromToken` to swap
    /// @return toAmount the swapped amount of `toToken`
    function query(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view returns (uint256 toAmount);
    /// @notice Swap `fromToken` to `toToken`.
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of `fromToken` to swap
    /// @param minToAmount the minimum amount of `toToken` to receive
    /// @param to the destination address
    /// @param rebateTo the rebate address (optional, can be address ZERO)
    /// @return realToAmount the amount of toToken to receive
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address to,
        address rebateTo
    ) external returns (uint256 realToAmount);
    /// @notice Deposit the specified token into the liquidity pool of WooPPV2.
    /// @param token the token to deposit
    /// @param amount the deposit amount
    function deposit(address token, uint256 amount) external;
}
// 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
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/**
 * @dev Interface of the IOFT core standard
 */
interface ICommonOFT is IERC165 {
    struct LzCallParams {
        address payable refundAddress;
        address zroPaymentAddress;
        bytes adapterParams;
    }
    /**
     * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`)
     * _dstChainId - L0 defined chain id to send tokens too
     * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain
     * _amount - amount of the tokens to transfer
     * _useZro - indicates to use zro to pay L0 fees
     * _adapterParam - flexible bytes array to indicate messaging adapter services in L0
     */
    function estimateSendFee(uint16 _dstChainId, bytes32 _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee);
    function estimateSendAndCallFee(uint16 _dstChainId, bytes32 _toAddress, uint _amount, bytes calldata _payload, uint64 _dstGasForCall, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee);
    /**
     * @dev returns the circulating amount of tokens on current chain
     */
    function circulatingSupply() external view returns (uint);
    /**
     * @dev returns the address of the ERC20 token
     */
    function token() external view returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}