ETH Price: $2,548.25 (+0.35%)

Transaction Decoder

Block:
19569085 at Apr-02-2024 03:46:11 PM +UTC
Transaction Fee:
0.122635857523158308 ETH $312.51
Gas Used:
2,131,667 Gas / 57.530494924 Gwei

Emitted Events:

185 SaleProcessor.RoleGranted( role=0000000000000000000000000000000000000000000000000000000000000000, account=[Sender] 0xf4ae5530d63dc4b66b76b3220021dd30f8309f3f, sender=[Sender] 0xf4ae5530d63dc4b66b76b3220021dd30f8309f3f )
186 SaleProcessor.RoleGranted( role=241ECF16D79D0F8DBFB92CBC07FE17840425976CF0667F022FE9877CAA831B08, account=[Sender] 0xf4ae5530d63dc4b66b76b3220021dd30f8309f3f, sender=[Sender] 0xf4ae5530d63dc4b66b76b3220021dd30f8309f3f )

Account State Difference:

  Address   Before After State Difference Code
0x482667A6...f1ed62B27
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 68130861475952810931858268914900833868808518356779120911230097053528732768657095249718656330192360630702604385878548482171880257560960112433341083299952487273494122721642969302727974664391330805288848140500068442551786689447499254667117923563565974830221445908706632507445459973079348808745206637624038942486177875398880275376963461751051335339185469950921775933388871253531941804078830575506218491712588837952438195801204583545218505053603282021254224846054332027206736081591739024165465710150807987549990236665662086111951037743360128115646370984410465010809423870172601801162307876179583380138631317709032312533841283773881285458986541793116315340831105384415448382672845615463868430658722515313130777508528112266139791983663317940041597407085089518372912321394871470183143190729126243722464805284178197033721399764180613929586986160031892099178881069169892759303631542647659288401350822238743402850780420665353655124399344239998933733427808064646363558597707166648279223570049086554837326229657493247745923627111475597251727189590706927086048684707346701789318895619499666017691048673582744924609900557411635771924476294816756266878420010233276930318360061637785693433958461829157404273714899927987302010672426511703160287792997806985361622243458639661709358083053999923187828839108987995041135008661671716892088159557540313609347652964858804616061315365765650318402732865271321783589980471247297575690298220949980919828091404636413856283883949567328682219495521523720576405526438667043081641241341846578432292095466879668169443268167219311166212088753580711788617250363410982240775402927843757560356764546619222301556234718974329741399507661867989912036303210464517191834936337038074885132653880936875220087998195159926048643753203381313944079255134193155356706147884497116824978371178895888355529601116917256379777352368365536549933685397919512773022980834652067085642802017926672907407042678406737524643324408370641769842501178661393411541218604319321580774658498538703028916297473774592429325307756173703149122843929271567637833700980033343380648997845178435051545688422336116009832039942525890891056572637735833039591066438539882603077215184040939049168354629165353763771659228440652172699088006026886376753613426296641795305469717420894762084727120144421558802601066485603127289141954208955758928053671764912834501677911191166595849741946076935254644960788974474776661611815870442578965318848385598978363381252664477546689148161547882318139639407218566568468319268924593926450108654626115911019621229987436018598202300977472460770269817984280002083612738733702438384354588676095891945071879544889336228098941217916866216900072192348562382373423401420521606578714956968552572679295776602470168794078796499559039560773800457253050810947372911660175612220361958124711595791543869958991283245479512489308188125035424649602324446792430139245166788868546285991838718978786514012353962666004865658364486926083706435655480385219601979760906052848797911443907673294465952838191032848411897204309374177365279329296768001148042289287366169943198716025786518084800651517720740505568881972790467352305582341117897613430836376077957097859265406219351430375344313746074144361872301819311802000175462914017916771569989918429404026967988783634695048642148542081825574555830595754041513334214480754405509375236105467661495100788040876691214954377193986790990469010811394732618569633052322369937386256705416958316839301972946193296949338078788629977797458442941607121982173993050472860009874577154208224376912161993674111070362240024724454202350016686401483565674600600760780570278155838550063147131575518144088633532514670667051404436131722810804289106431541631846967229690588311832138112426498339183911503904703673226887709065704912125857379978110768848664927609279213322666108279802277073790293647452036164394929267602369590414642749628962620517404849115800595386260006878193807248759107801529720771993079844223758022565299426630339129320195340423587062440240281001897513289692385425885379927112611435843842037100110414319679875517863836351980182781642243739117593041857909215435259124776684264307487147103973662267324470017005744441973720107406067745563057691162633510747774646043806224326050500415007209565099953225206258476002491501394348103249082323822661200951940588954843414500910975758004062313661350316928445346301106830833914373822354029483161421654206787618876666890669622075488965689377748579047864940361650014906167109222310993464519425503262134623089077943308947138649302292409508002445679473709728414863156998519602061296313038611438638089752391399742283783930691847297679794456302280252640820992703482394138262701800022925058947316563438226476642739516329880276144753203454726956564121312893907775226221364932839556861329775436260390568207029866710108077391528079415432427336046831089795767398784263210854070069397557432317490422145939482851115958055985461513332469850635369453375815122225958164782385093596562990522311748564149851495143955611862030329595678074990062183147173607264349059845047539481183103552413432455574062275672390726850329466257570305371227286150135710606432763752077873693504016914316168717808547922738572649249011118837578766029138972172023944156483754132497171019558432640686637596482110730238440371992077290708592541310955280086165233221543135709829927259870713375522063227046879186851157582680221583887104085869007313783502547820850819833363712235702883687911914272745079671255866877764094186189165521971943523662341498663423529876573288490436147297552454541095191871457895086558289948482255266650656920044985552141042486530514910297426455156925122465198305326833814018545311010697144391813312767173693113940051084092773909091496336816107389326624168549447270704528808673824005223001522656507623017913350482623754386257134789356562020379520046087370995294939279920022644400339254243039405384735311457103437685320769859883519909309604870132667916173650794216610259266626303288822685808375853164058729776595382163008745546763069692505413205834177624914020819799689057319547514476035263842245952373887481230668404099329473435906407261144375159364804434019171890214097795130077943878828917769530424950654983424208017817389940405403379966282501525098996135713848900339408456462872107227056029521480592770205001353966750872224083372285867436588339187456673425855025615077494569695026717981522374620291021043590555260504723707445211350953900734254067775325621358089531356320302009099011000707035739628245015497272695757045001084996623048768381147241047198257753908823934849514483397708231915808674122783501880197397446215332910594564194904104417126774199706886946951471070525486359851159754107191329601994600345487349786408281393105249684021191601260597388267447722823859775591414777841753816987248802252291263791193831321919863043190391777172898745105475529486974378341809923425682714518612183458271338947183415218569638434734696351367623644216721982213961153214215096672742264042404478329081662852887388499899929318934113340541757683617773829056379186251651423931591715683737040100958710557712541714217994836008079839881254489658942531535104954098815840778419419194505922756804484763398337490353065617047877872975546928705134059558027075337928560839292535496570237117359427866002108029834369247560043685942607741210758097375854577447974278672936087290598292696607014097367892663975972222317177606207702111868887118065784619732815864264690975799825134087227666096313894951022222832030221781319439794981603924597133152703107830675576074395139215835645592129061623690769891252382591941592453141128321230101342941497928080110867851603107497863155805871855181816900734044824138768255131833071104916948074570786758942587366863669326490208708329300105483824414444410313810138522010923615586016951230294189654844533959243014028872904398445091295243036878001750737228519578255317503587858109922157362395514045970073846552252804012986762358879989458720084281481732464867106917872011252752686703673448566115993664791276863973931558267100187368124867998350421837845391423898310084258460206235022727537693122907735518031775559282965779542410946955020000023213701123794720148803096999042029924816659826234773083694784368071207114511850067040076815395017163703897807431025776234906557689126666225281874542952635864014779947317049401382232345238137785784268314036093273932406373874254484818148244565939300430307418006703195036372772218740575561209020605278215569790649688130416811547470246682561820366823131860913582233068035877969956554146073301800778742415245023557106677827691084603452017013072663669676966294607646507745724024573971119848787523372189156616302330078912383680046983371982615356704663363937899962602691720525023328642772307037340508029629160296321708788820130498701966260616171339204754970827931968803933673642267583952294799809442951265504381870750521812564862448243139592545853354709683735537413251580361181170462596775008881188589089790763644436565187723734070373834612937710934305407668656644060252999477311217219773097077940496584436011175592361026526857053997439855380919820796254240019656623271755923497296083416474633134844831406788761013969645920067617442159282604953565838551075516302904294966930415248732880146279756624609108330764920228175730999458081788860818381588490986581207488619125861745756959395958221775399947131791894234847473303882017336162722881232135569072669474587823538439506073541271283338407481009158262298693027105628504024510507193843078740727858022169054094363292803158773991065640480538129790241245675805854334086261757121739974959879615778524476220426026963798297789906286152607057052001422403342754773915225991205483672420587732495497465676590437284936106258714227759143209864154395074703229432625598702153376294817526662023535040816500828833984522099046214621802841155091376256253043704577212694534188529580927300293194735359456638287074183575886476120700230046027112080609174302337110903812341107189432189549028648341317126840580371075012477856327442927545067584325103715792181188495812003450092801322706191330722622243168150068377935567527173983155246708595669205055848653575172538494569009429417552669228849756461740130421577798963453082257499411776466660578225279652486473778859671015084797495176535154775853315742175630433063260762861094159941426744384676123235372369611843912579039095557505470427468453399750223878049286415687020307242377177186265950047079464136999087405032252625930871679974509259088854922066374372220643433379803533387725020735790629626617597348232133353655358344613373562965326032901346655597425102942017541013941333495279370736914956495104815274826834973508713102066098405911594117300808839483675890808061061628908899866684028403777465049096246142676670881360930210279598430701033866815098758541584970378657613659216827052361233165367910238899888928222080349223745557752674317851702167428511343152195795722991106714466252139009057961164697220648022424038044554754267759636590251888435401100454935054407854308821069583987842966763285035157575277795225898702805067577372045273578455598955680747996954782053675402086697903687923662363677901230348366257811649678291155552835992623708688589641749762642945226192004397069078381901592154503035975488478294166486013345860540426581383660197539325420449058666890247520315587519913243809742063442782457663078179670167174257382870661625743703089611852996193423594620901998962313555923020169899135903313445283049307498685888945241940089930159810636239394193907717412289223609678967897933084532861030069460623115758496514881392357549904950467219001603613113912973599295242160447759831323668733763901861671745368039568864870964260330453857425894839359144938484304676047435972251819680098143238161460719285736053038407173241203125502549042611445864713996851550221164831544663670862601273178838869781867420701144429169908574066613928422846320986408382970806729445483735490276241571292487568419508407125104839691995754637422733960424024856152572276154755943805555139194508076906288362363494122973648548725228850294990942619986488185407678021759042826191978084081712629596488323476835589306588848265288964852943194406726567986201414189841906304128564331426159727619925281887364858795148275581649202436776242550246421676760574245020112408391443527871543269349394917091229882794453716159842492014367468148752776250591631069325125948106721404957563064268041110426266870218722486428492842986199845532454362918451411261195966016370614090220674841280963278064937380652186904183223938732203607749137949602854610429303288829924543155842013705649965216459395596982436855280955489387786854810146904475933872578297833435907817825426860166182791039960738323028015969440348005673275158014747070725897197974250740511907020653002300162733479430741758102903858421145505334722821239237666032019189928147770423062494414115314744329889465290164211076548882377680556684531746858130784529842688932509320834057781291219644004673034579386755377691010315045058688702908943031549868620948563190009914794635618268476305958878373923989782525757646162245148544917506887465511029266085464507859264008342895440905257523759589633954156716850585746438382827334887248669473161732091522929143968909195425395819048808365403356764030861888411595344850908120296845373171595191304221892152549386978078136836958255853813942143201599228850128075382518111843286937141434769492133589460596904842885977899476304606447289942109344154721404614327604692473864359486895100092925157109645144629452761640243641810904898510821491960316203555742184706453855738843485030879649721486330210156057080317882683196170919315542208425757256198510758385923535480623063863024322155215736726455127224879621109611779117844383684694960884633748776029862028574517848003698345405079094364799295017626795960897239598072978512589606232491827536597325254913795147695847448609080677182359524646197819692948267130984731438672674510387338062186714639297367823247700906513313565066378470154283348899024808927394024812220423777211217652146349956488970312827152598906363079172530165820284785858747309821255641323910881887756031203388830618884113261172694100460196916033993576489426341377241507333600787252348088220478144972855333781331752193067319547948452296481712891076947310344272761599559912535439804406580715605776572355503755448874506643499436931748751280473461715973170063237002919226346788044724163508885213554441570630580050234212285970054815564271156090685102142684638664303747598942859431938988472925361305721044622497727511217898424755687730515703257924615720199434684930638193117348786444500891035771801697559676688186646349172369956454244692194998713972459019856438121269092457314461607628370514489311030979014358944833661110317995428434993896839256375244205003305267319384595369229540181434846636379667773619439889303870994029950543941233748323496082514065170609753887835528236805891663956961189760127288805082909901594738387898078248993009067259374630358361272904584541841087140688236201196432425263429884338350871033167269685183242014953816335601881442244981641496148756148856046103958445672379557038814980215658884172301130829767709586609696773380530185706032668037061261011190753449152426506474046611065080259966689446290055052193935458446614775896929870912183559631443210314500336239959091786033376749028807903890600336871508355594629343155756032150899167661546436935667901552900007090489651744458648495649001295110169038242533688910611396371635914980441582851311413985719396164428964501362808233335646386710971243612580877144810421648684613873867067902049457900187889291730390159716661848611525998869339210986577560512261241936861717585756898823334810774160944285372916940178322665568768200048057792395356602601453809796485814323254656496288754749752335824161780687288372984310486540735999030365121206254732381713468311418090110969644724542257940536417852547059160993325439300332412600236863246836029597304093367134193923472587223211955219586897154559514332487922577287176748199049673617758923937357447625367080703688442250946953560072792875722085012647776286733049087014847972428441768338703405857978724499174523381959558549081278079427874692446442290848139616431901728751499059217011936921415385855609078995448762933670915023525234593775818602161875166151519678887427419994723696383050018562905011033208441589389404788185744946563710444781762221859925870132885657074107562833803602045384152207023266184503492220403619691780058500047318091789082735503739698837117731982318505411852510094814864460569006307105125113696434641311722006258336687401267448415570114218334088349435447832315187258403665479692280614646351197279952141404088474784180203735258834625666958840471880877006503542297610021994644762452853695954176912174565451811828560743986293076511165253471251712212689834797158742817711955916547230823557266727615480879500923452581614437039801452780306637896525655067658384768062743370126289078244376298155472271108991941275173594381447319537349262540561254278726643037892461003092710471940998981943878220986791789602803770606948477620067495486461347322004597288205236479367616830728207340072066935805413876320207316971359729555829827484379526104418244292857328518408398086326357422072645016591386292667800640868092149332765757904444677902742190471820575988312629195958348579904663779621969942779903975876747769424779937533632941360591785068310200826572521907219886592015946219396351352842348041870292636037665571466793518626221838648130925631917664762279920137057054958094193345466202767460638491798078078192662642622570540269434776166788371688975489807296960403222925371632277386834808011306392707749217888676965437142752326140361037690476670944377183187271739488965143114613452875373195710332417313938947026800428544819375665896613885496760370644168052265456209219059882326380709418845556878740918888962620894522655953874774323175769994032435483618489485245437769032969660757190110602895705205962964783497437416081307517556202380914017429499085717417265317751804731054531989525501486071029614387332623634578690919726368778592319900220201913201862040455822141216747384739310105168726978204857138853123531260933561284933279574905276547948619440168489422312792905128310138515954147479835553636922300299734042184565927182104874311148908736912014915172376468538118800768475752819997939544025336969988935253445343310200785804451315744334507942944562844420968293829956257407944145398461354302240567115130401254609371941182637190337166779923934055155146473157408394002015032705050528729495100463624273718516966157027536813016401725176495614156942371670693044660081851519244174947404968823584530744274441871923215074602613970485428842456511235040810180120233907050780313274575248965803370365401202741361622640116050850746283450743267999217468754483896101288266433894718336768379799563770377510057613412111900083822094934777788270519012305610125709072594855881679553978987036347634509237703457541380858335152187287583199627534338608581264036121581724252787741435803460636880726870369975486659477462959230907161903780480219392570524949369222485755976931737055445727978812717021032348528509733747461225004389484146025600809530211241044888424345124191609046149030637091760917154742740302729310177845904695041838499151917054590659453869298887145631102663178804458308302516004310471442756888022825821843015729511070197854112971851702840343920919177022566809223561506885143456626304865192665650095959834626159113190631299859476220505940786917739176431441933793505159392882990958231024451672970250423646640519470624458427119938052119455624363000573131087142616610587520447672043057848088038892609942379908556740525808002272224286528049706111956358949660157620607566697866484624988388768755988802267556884711063738573706693571665648924223879771570625719784638820352048166188000579202489154810202333443927068895758865528510380407478831584269072369517670000832176080458782655123252670927859699316063475987376677025679638484648285870381909125006477741793597262986735947734704667916039225004662136567326885509130546544651532832731787548054709487647139089220364067406812488830761083474447224353061444197864496298650484929732113428703718401392574550452188670715251182787977648059873338768377779069065745425377517128213707460360023876992983461565039304691755855967832903571060459205434346650940752114405031918192481443254395004816712721811209313634365693391371087191216906256629163196962822317363094424101774348173157052286597137299513551348330404945824282339023671920046941829663464166840474391093021884904749265789598843270753776273853477844634741145553904543784893248467365102169630847537393902770921107548598954938983691603344910315558079645727048477406512845237717975855265414671291198762176410689035152399264782281477137090614440603978627151664715499246755398009334618403247871268037437742172132912001118829367240391852210055748860068840714355281621536412701447826800628911614481128809905128944915580535102040333789099551372078056763853315433185829093685196242700114918761850120597001632629110231880532508668895382444842719705847004458143188525723504968167264039715742323258935751696865267816766581688551833597649562538004749873141591450438818890303121171402748435281987826801041200744788119348758360286111690048077440487235563690192900451319399677981792381475742997209168309715463099244225272055743150384577161283904694212298356190421773529810198687604283261248742821472584323527457587252368169307571077287656873015872138387048508663844218220610544019838488965699005967598485786593252604373775920913080166820008647186418058442635761528467339835081138503023171479184521310949780572701984072398029374604497528787195815464421898369286064131940016200220220117596046164019
(Titan Builder)
5.554607252988822559 Eth5.556717603318822559 Eth0.00211035033
0xf4ae5530...0f8309F3F
0.34478 Eth
Nonce: 0
0.222144142476841692 Eth
Nonce: 1
0.122635857523158308

Execution Trace

SaleProcessor.60803462( )
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }
    mapping(bytes32 role => RoleData) private _roles;
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }
    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }
    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }
    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }
    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }
    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }
    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }
    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }
        _revokeRole(role, callerConfirmation);
    }
    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }
    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);
    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;
    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;
    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);
    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);
    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);
    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;
    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);
    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }
    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }
    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }
    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }
    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }
    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.
        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }
    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.
        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);
    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();
    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }
    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }
    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }
    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }
    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }
    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
 * @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;
    }
    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
 * @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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }
    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }
    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }
    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }
    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }
    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }
    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }
    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }
            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }
            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }
            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////
            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)
                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }
            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)
                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)
                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }
            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;
            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;
            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256
            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }
    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }
    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);
        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }
    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }
    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }
    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }
    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
 * @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;
    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();
    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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }
    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }
        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }
    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }
    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
// SPDX-License-Identifier: NONE
pragma solidity ^0.8.21;
interface ISaleProcessor {
  enum Option { Short, Long }
  enum State { None, Opened, Closed }
  struct Stage {
    bool defined;
    State state;
    uint256 sPrice;
    uint256 lPrice;
    uint256 sold;
    uint256 supply;
  }
  struct Ambasador {
    bool defined;
    bool enabled;
    uint256 firstRefRate;
    uint256 secondRefRate;
  }
  function isSaleActive() external view returns (bool);
  function getStageInfo(uint256 index_) external view returns (Stage memory);
  function getCurrentStageIndex() external view returns (uint256);
  function regularDepositLimit(address receiver_) external view returns (uint256);
  function maxDepositLimit(address receiver_) external view returns (uint256);
  function getMinimalDeposit() external view returns (uint256);
  function getAmbasador(address receiver_, address ambasador_) external view returns (address);
  function getAmbasadorRate(address ambasador_) external view returns (uint256, uint256);
  function getCurrentPrice(Option option_) external view returns (uint256);
  function getBank() external view returns (address);
  function process(address receiver_, address token_, uint256 amount_, uint256 sold_, address ambasador_, uint256 fReward_, uint256 sReward_) external;
}
// SPDX-License-Identifier: NONE
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/AccessControl.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/utils/math/Math.sol';
import './interfaces/ISaleProcessor.sol';
contract SaleProcessor is ISaleProcessor, ReentrancyGuard, AccessControl {
  using SafeERC20 for IERC20;
  
  address internal constant tokenAddress = 0x9000000000000000000000000000000000000009;
  address internal constant ethAddress = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  uint256 private _vipLimit;
  uint256 private _totalReleasedTokens;
  uint256 private _cAmbRate = 50;
  uint256 private _tAmbRate = 50;
  address private _bank;
  State private _saleState;
  Stage[] private _stages;
  uint256 private _currentStage;
  
  uint256 private _maxDeposit;
  uint256 private _minDeposit;
  bytes32 public constant MANAGER_ROLE = keccak256('MANAGER_ROLE');
  mapping(address => uint256) private _totalBalances;
  mapping(address => mapping(uint256 => uint256)) private _stageBalances;
  mapping(address => bool) private _vip;
  mapping(address => Ambasador) private _ambasadors;
  mapping(address => address) private _ambUsers;
  mapping(address => mapping(address => uint256)) _ambBalances;
  event Claimed(address indexed ambasador, address indexed asset, uint256 amount);
  constructor(address bank_) {
    require(bank_ != address(0), "SaleProcessor: zero bank address");
    _bank = bank_;
    _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
    _grantRole(MANAGER_ROLE, _msgSender());
  }
  function getBank() external view returns (address) {
    return _bank;
  }
  function getMaximalDeposit() external view returns (uint256) {
    return _maxDeposit;
  }
  function getMinimalDeposit() external view returns (uint256) {
    return _minDeposit;
  }
  function getStagesAmount() external view returns (uint256) {
    return _stages.length;
  }
  function getCurrentStageIndex() external view returns (uint256) {
    return _currentStage;
  }
  function getStageInfo(uint256 index_) external view  returns (Stage memory) {
    return _stages[index_];
  }
  function getTotalSold() external view returns (uint256) {
    return _totalReleasedTokens;
  }
  function balanceOf(uint256 round_, address user_) external view returns (uint256) {
    return _stageBalances[user_][round_];
  }
  function ambasadorBalanceOf(address asset_, address user_) external view returns (uint256) {
    return _ambBalances[user_][asset_];
  }
  function regularDepositLimit(address user_) external view returns (uint256) {
    uint256 amount = _totalBalances[user_];
    uint256 limit = _vipLimit;
    if (isVip(user_)) {
      limit = _maxDeposit;
    }
    return amount < limit ? limit - amount : 0;
  }
  function maxDepositLimit(address user_) external view returns (uint256) {
    uint256 amount = _totalBalances[user_];
    return amount < _maxDeposit ? _maxDeposit - amount : 0;
  }
  function getVipLimit() external view returns (uint256) {
    return _vipLimit;
  }
  function getAmbasadorRates() external view returns (uint256, uint256) {
    return (_cAmbRate, _tAmbRate);
  }
  function getAmbasador(address user_, address amb_) external view returns (address) {
    Ambasador memory ref = _ambasadors[_ambUsers[user_]];
    if (ref.defined && ref.enabled) {
      return _ambUsers[user_];
    }
    ref = _ambasadors[amb_];
    if (!ref.defined || ref.enabled) {
      return amb_;
    }
    return address(0);
  }
  function getAmbasadorRate(address amb_) external view returns (uint256, uint256) {
    Ambasador memory ref = _ambasadors[amb_];
    if (ref.defined) {
      return (Math.max(ref.firstRefRate, _cAmbRate), Math.max(ref.secondRefRate, _tAmbRate));
    }
    return (_cAmbRate, _tAmbRate);
  }
  function isSaleActive() public view returns (bool) {
    return _saleState == State.Opened;
  }
  function isSaleInactive() public view returns (bool) {
    return _saleState == State.Closed;
  }
  function getCurrentPrice(Option option_) public view returns (uint256) {
    if (_stages[_currentStage].state == State.Opened) {
      return option_ == Option.Short ? _stages[_currentStage].sPrice : _stages[_currentStage].lPrice;
    }
    return 0;
  }
  function isVip(address user_) public view returns (bool) {
    return _vip[user_];
  }
  function openSale()
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(_saleState == State.None, "SaleProcessor: sale already started");
  
    _saleState = State.Opened;
  }
  function closeSale()
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(isSaleActive(), "SaleProcessor: sale not started");
    _saleState = State.Closed;
  }
  function setStage(uint256 sPrice_, uint256 lPrice_, uint256 supply_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(!isSaleInactive(), "SaleProcessor: sale closed");
    _stages.push(
      Stage({ defined: true, state: State.None, sPrice: sPrice_, lPrice: lPrice_, sold: 0, supply: supply_})
    );
  }
  function setAmbasadorRate(uint256 firstAmbRate_, uint256 secondAmbRate_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(!isSaleInactive(), "SaleProcessor: sale closed");
    require(firstAmbRate_ <= 1000, "SaleProcessor: cant set first rate more then 100%");
    require(secondAmbRate_ <= 1000, "SaleProcessor: cant set second rate more then 100%");
    _cAmbRate = firstAmbRate_;
    _tAmbRate = secondAmbRate_;
  }
  function setupAmbasadors(address[] calldata ambs_, uint256[] calldata firstAmbRates_, uint256[] calldata secondAmbRates_)
    external onlyRole(MANAGER_ROLE)
  {
    require(!isSaleInactive(), "SaleProcessor: sale closed");
    require(ambs_.length == firstAmbRates_.length && ambs_.length == secondAmbRates_.length, "SaleProcessor: invalid arrays length setup");
    for (uint256 index = 0; index < ambs_.length; index++) {
      _ambasadors[ambs_[index]] = Ambasador({
        defined: true,
        enabled: true,
        firstRefRate: firstAmbRates_[index],
        secondRefRate: secondAmbRates_[index]
      });
    }
  }
  function updateStagePrice(uint256 index_, uint256 sPrice_, uint256 lPrice_) 
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(!isSaleInactive(), "SaleProcessor: sale closed");
    require(_stages[index_].defined, "SaleProcessor: stage should be defined");
    require(_stages[index_].state == State.None, "SaleProcessor: stage should not be started");
    _stages[index_].sPrice = sPrice_;
    _stages[index_].lPrice = lPrice_;
  }
  function updateStageSupply(uint256 index_, uint256 supply_) 
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(!isSaleInactive(), "SaleProcessor: sale closed");
    require(_stages[index_].defined, "SaleProcessor: stage should be defined");
    require(_stages[index_].state != State.Closed, "SaleProcessor: stage should not be closed");
    require(_stages[index_].sold < supply_, "SaleProcessor: new supply must be bigger then sold tokens");
    _stages[index_].supply = supply_;
  }
  function startStage(uint256 index_)
    external onlyRole(MANAGER_ROLE)
  {
    require(isSaleActive(), "SaleProcessor: sale not active");
    require(_stages[index_].defined, "SaleProcessor: stage should be defined");
    require(_stages[index_].state == State.None, "SaleProcessor: stage should not be used");
  
    if (_stages[_currentStage].state == State.Opened) {
      _stages[_currentStage].state = State.Closed;
    }
    _stages[index_].state = State.Opened;
    _currentStage = index_;
  }
  function finishStage(uint256 index_)
    external onlyRole(MANAGER_ROLE)
  {
    require(_stages[index_].defined, "SaleProcessor: stage should be defined");
    require(_stages[index_].state == State.Opened, "SaleProcessor: stage should be active");
    _stages[index_].state = State.Closed;
  }
  function setupVip(address user_, bool value_)
    external onlyRole(MANAGER_ROLE)
  {
    _vip[user_] = value_;
  }
  function setupVipBatch(address[] calldata users_, bool[] calldata values_)
    external onlyRole(MANAGER_ROLE)
  {
    require(users_.length == values_.length, "SaleProcessor: invalid arrays length setup");
    for (uint256 index = 0; index < users_.length; index++) {
      _vip[users_[index]] = values_[index];
    }
  }
  function setMaxDeposit(uint256 amount_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(amount_ <= 10000000000000000000000000, "SaleProcessor: value is too big");
    require(amount_ >= _minDeposit, "SaleProcessor: value is too small");
    _maxDeposit = amount_;
  }
  function setMinDeposit(uint256 amount_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(amount_ >= 100000000000000000, "SaleProcessor: value is too small");
    require(amount_ <= _maxDeposit, "SaleProcessor: value is too big");
    _minDeposit = amount_;
  }
  function setVipLimit(uint256 amount_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(amount_ >= _minDeposit && amount_ <= _maxDeposit, "SaleProcessor: invalid value");
    _vipLimit = amount_;
  }
  function setBank(address bank_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(bank_ != address(0), "SaleProcessor: zero bank address");
    _bank = bank_;
  }
  function process(address user_, address asset_, uint256 amount_, uint256 sold_, address amb_, uint256 fReward_, uint256 sReward_)
    external onlyRole(MANAGER_ROLE)
  {
    _totalBalances[user_] = _totalBalances[user_] + amount_;
    _totalReleasedTokens = _totalReleasedTokens + sold_;
    _stages[_currentStage].sold = _stages[_currentStage].sold + sold_;
    _stageBalances[user_][_currentStage] = _stageBalances[user_][_currentStage] + sold_;
    if (amb_ != address(0)) {
      if (!_ambasadors[amb_].defined) {
        _ambasadors[amb_].defined = true;
        _ambasadors[amb_].enabled = true;
      }
      _ambBalances[amb_][asset_] += fReward_;
      _ambBalances[amb_][tokenAddress] += sReward_;
      _ambUsers[user_] = amb_;  
    }
  }
  function enableAmbasador(address amb_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(_ambasadors[amb_].defined, "SaleProcessor: ambasador not defined");
    require(!_ambasadors[amb_].enabled, "SaleProcessor: ambasador already enabled");
    _ambasadors[amb_].enabled = true;
  }
  function disableAmbasador(address amb_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    require(_ambasadors[amb_].defined, "SaleProcessor: ambasador not defined");
    require(_ambasadors[amb_].enabled, "SaleProcessor: ambasador already disabled");
    _ambasadors[amb_].enabled = false;
  }
  function claimAmbasadorRewards(address[] calldata assets_)
    external nonReentrant()
  {
    address amb = _msgSender();
  
    require(assets_.length > 0, "SaleProcessor: no assets to process");
    require(_ambasadors[amb].defined, "SaleProcessor: ambasador not defined");
    require(_ambasadors[amb].enabled, "SaleProcessor: ambasador not enabled");
    for (uint256 i = 0; i < assets_.length; i++) {
      address token = assets_[i];
      uint256 balance = _ambBalances[amb][token];
      if (balance == 0) { continue; }
      _ambBalances[amb][token] = 0;
      if (token == ethAddress) {        
        (bool success, ) = amb.call{value: balance}('');
        require(success, "SaleProcessor: native claim error");
      } else {
        IERC20(token).safeTransfer(amb, balance);
      }
      
      emit Claimed(amb, token, balance);
    }
  }
  function recoverNative()
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    uint256 balance = address(this).balance;
    _msgSender().call{value: balance}('');
  }
  function recoverErc20(address asset_, uint256 amount_)
    external onlyRole(DEFAULT_ADMIN_ROLE)
  {
    IERC20(asset_).safeTransfer(_msgSender(), amount_);
  }
  receive() external payable { }
}